Add archived document PDF viewing and download route
This commit is contained in:
parent
b3e626b613
commit
a506eba604
|
|
@ -1,8 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Filament\Pages\Strumenti;
|
namespace App\Filament\Pages\Strumenti;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use App\Models\Documento;
|
use App\Models\Documento;
|
||||||
use App\Models\MovimentoContabile;
|
use App\Models\MovimentoContabile;
|
||||||
use App\Models\Stabile;
|
use App\Models\Stabile;
|
||||||
|
|
@ -10,13 +8,14 @@
|
||||||
use App\Services\Documenti\PdfTextExtractionService;
|
use App\Services\Documenti\PdfTextExtractionService;
|
||||||
use App\Support\StabileContext;
|
use App\Support\StabileContext;
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Dompdf\Dompdf;
|
use Dompdf\Dompdf;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Forms\Components\DatePicker;
|
use Filament\Forms\Components\DatePicker;
|
||||||
use Filament\Forms\Components\FileUpload;
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Pages\Page;
|
use Filament\Pages\Page;
|
||||||
use Filament\Tables\Columns\BadgeColumn;
|
use Filament\Tables\Columns\BadgeColumn;
|
||||||
|
|
@ -30,12 +29,16 @@
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Livewire\Attributes\Url;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class DocumentiArchivio extends Page implements HasTable
|
class DocumentiArchivio extends Page implements HasTable
|
||||||
{
|
{
|
||||||
use InteractsWithTable;
|
use InteractsWithTable;
|
||||||
|
|
||||||
|
#[Url( as : 'tab')]
|
||||||
|
public string $hubTab = 'archivio-digitale';
|
||||||
|
|
||||||
protected static ?string $navigationLabel = 'Documenti (PDF)';
|
protected static ?string $navigationLabel = 'Documenti (PDF)';
|
||||||
|
|
||||||
protected static ?string $title = 'Documenti (PDF)';
|
protected static ?string $title = 'Documenti (PDF)';
|
||||||
|
|
@ -48,20 +51,26 @@ class DocumentiArchivio extends Page implements HasTable
|
||||||
|
|
||||||
protected static ?string $slug = 'strumenti/documenti';
|
protected static ?string $slug = 'strumenti/documenti';
|
||||||
|
|
||||||
protected string $view = 'filament.pages.gescon.table';
|
protected string $view = 'filament.pages.strumenti.documenti-archivio';
|
||||||
|
|
||||||
public static function canAccess(): bool
|
public static function canAccess(): bool
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
return $user instanceof User
|
return $user instanceof User
|
||||||
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
|
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
|
$this->hubTab = $this->normalizeHubTab($this->hubTab);
|
||||||
$this->mountInteractsWithTable();
|
$this->mountInteractsWithTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function selectHubTab(string $tab): void
|
||||||
|
{
|
||||||
|
$this->hubTab = $this->normalizeHubTab($tab);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
@ -79,8 +88,8 @@ protected function getHeaderActions(): array
|
||||||
->form([
|
->form([
|
||||||
Select::make('stabile_id')
|
Select::make('stabile_id')
|
||||||
->label('Stabile')
|
->label('Stabile')
|
||||||
->options(fn () => $this->getStabiliOptions())
|
->options(fn() => $this->getStabiliOptions())
|
||||||
->default(fn () => $this->getDefaultStabileId())
|
->default(fn() => $this->getDefaultStabileId())
|
||||||
->searchable()
|
->searchable()
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('faldone')
|
TextInput::make('faldone')
|
||||||
|
|
@ -114,7 +123,7 @@ protected function getTableQuery(): Builder
|
||||||
|
|
||||||
$allowedIds = StabileContext::accessibleStabili($user)
|
$allowedIds = StabileContext::accessibleStabili($user)
|
||||||
->pluck('id')
|
->pluck('id')
|
||||||
->map(fn ($v) => (int) $v)
|
->map(fn($v) => (int) $v)
|
||||||
->values();
|
->values();
|
||||||
|
|
||||||
if ($allowedIds->isEmpty()) {
|
if ($allowedIds->isEmpty()) {
|
||||||
|
|
@ -141,7 +150,7 @@ public function table(Table $table): Table
|
||||||
BadgeColumn::make('tipo_documento')
|
BadgeColumn::make('tipo_documento')
|
||||||
->label('Categoria')
|
->label('Categoria')
|
||||||
->toggleable()
|
->toggleable()
|
||||||
->formatStateUsing(fn ($state, Documento $record) => $state ?: ($record->tipologia ?: '—')),
|
->formatStateUsing(fn($state, Documento $record) => $state ?: ($record->tipologia ?: '—')),
|
||||||
TextColumn::make('descrizione')
|
TextColumn::make('descrizione')
|
||||||
->label('Descrizione')
|
->label('Descrizione')
|
||||||
->limit(50)
|
->limit(50)
|
||||||
|
|
@ -180,46 +189,130 @@ public function table(Table $table): Table
|
||||||
->options($this->getCategorieOptions()),
|
->options($this->getCategorieOptions()),
|
||||||
])
|
])
|
||||||
->actions([
|
->actions([
|
||||||
|
Action::make('visualizza_pdf')
|
||||||
|
->label('Vedi PDF')
|
||||||
|
->icon('heroicon-o-eye')
|
||||||
|
->visible(fn(Documento $record) => $this->hasPdf($record))
|
||||||
|
->modalHeading(fn(Documento $record): string => trim((string) ($record->nome ?: 'Documento PDF')))
|
||||||
|
->modalWidth('7xl')
|
||||||
|
->modalContent(function (Documento $record) {
|
||||||
|
$openUrl = route('filament.documenti.download', $record);
|
||||||
|
|
||||||
|
return view('filament.modals.pdf-viewer', [
|
||||||
|
'url' => $openUrl . '?inline=1',
|
||||||
|
'openUrl' => $openUrl,
|
||||||
|
'title' => trim((string) ($record->nome ?: 'Documento PDF')),
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
->modalSubmitAction(false)
|
||||||
|
->modalCancelActionLabel('Chiudi'),
|
||||||
Action::make('scarica_pdf')
|
Action::make('scarica_pdf')
|
||||||
->label('PDF')
|
->label('PDF')
|
||||||
->icon('heroicon-o-arrow-down-tray')
|
->icon('heroicon-o-arrow-down-tray')
|
||||||
->url(fn (Documento $record) => route('filament.documenti.download', $record), true)
|
->url(fn(Documento $record) => route('filament.documenti.download', $record), true)
|
||||||
->visible(fn (Documento $record) => $this->hasPdf($record)),
|
->visible(fn(Documento $record) => $this->hasPdf($record)),
|
||||||
Action::make('etichetta_11354')
|
Action::make('etichetta_11354')
|
||||||
->label('Etichetta 11354')
|
->label('Etichetta 11354')
|
||||||
->icon('heroicon-o-tag')
|
->icon('heroicon-o-tag')
|
||||||
->url(fn (Documento $record) => route('filament.documenti.etichetta', $record) . '?format=11354&dymo_fix=0&dymo_rot=0&dymo_dx=0&dymo_dy=3&autoprint=1', false),
|
->url(fn(Documento $record) => route('filament.documenti.etichetta', $record) . '?format=11354&dymo_fix=0&dymo_rot=0&dymo_dx=0&dymo_dy=3&autoprint=1', false),
|
||||||
Action::make('etichetta_11354_driver_verticale')
|
Action::make('etichetta_11354_driver_verticale')
|
||||||
->label('Etichetta 11354 (driver verticale)')
|
->label('Etichetta 11354 (driver verticale)')
|
||||||
->icon('heroicon-o-tag')
|
->icon('heroicon-o-tag')
|
||||||
->url(fn (Documento $record) => route('filament.documenti.etichetta', $record) . '?format=11354&dymo_fix=1&dymo_rot=-90&dymo_dx=0&dymo_dy=0&autoprint=1', false),
|
->url(fn(Documento $record) => route('filament.documenti.etichetta', $record) . '?format=11354&dymo_fix=1&dymo_rot=-90&dymo_dx=0&dymo_dy=0&autoprint=1', false),
|
||||||
Action::make('etichetta_99014')
|
Action::make('etichetta_99014')
|
||||||
->label('Etichetta 99014')
|
->label('Etichetta 99014')
|
||||||
->icon('heroicon-o-tag')
|
->icon('heroicon-o-tag')
|
||||||
->url(fn (Documento $record) => route('filament.documenti.etichetta', $record) . '?format=99014&autoprint=1', false),
|
->url(fn(Documento $record) => route('filament.documenti.etichetta', $record) . '?format=99014&autoprint=1', false),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getCategorieOptions(): array
|
private function getCategorieOptions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'contratto' => 'Contratti (CONTR)',
|
'contratto' => 'Contratti (CONTR)',
|
||||||
'assicurazione' => 'Assicurazioni (ASSIC)',
|
'assicurazione' => 'Assicurazioni (ASSIC)',
|
||||||
'certificato' => 'Certificati (CERT)',
|
'certificato' => 'Certificati (CERT)',
|
||||||
'preventivo' => 'Preventivi (PREV)',
|
'preventivo' => 'Preventivi (PREV)',
|
||||||
'fattura' => 'Fatture (FATT)',
|
'fattura' => 'Fatture (FATT)',
|
||||||
'verbale' => 'Verbali (VERB)',
|
'verbale' => 'Verbali (VERB)',
|
||||||
'altro' => 'Altri (DOC)',
|
'altro' => 'Altri (DOC)',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHubStatsProperty(): array
|
||||||
|
{
|
||||||
|
$query = $this->getTableQuery();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'documenti' => (clone $query)->count(),
|
||||||
|
'archiviati' => (clone $query)->where('archiviato', true)->count(),
|
||||||
|
'in_scadenza' => (clone $query)->whereNotNull('data_scadenza')->whereDate('data_scadenza', '<=', now()->addDays(30)->toDateString())->count(),
|
||||||
|
'template_drive' => count($this->getDriveTemplateFoldersProperty()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int, string> */
|
||||||
|
public function getDriveTemplateFoldersProperty(): array
|
||||||
|
{
|
||||||
|
$items = config('netgescon.google.drive_template_folders', []);
|
||||||
|
$yearRoot = trim((string) config('netgescon.google.drive_template_year_root', ''));
|
||||||
|
$yearModel = trim((string) config('netgescon.google.drive_template_year_model', ''));
|
||||||
|
|
||||||
|
$folders = is_array($items)
|
||||||
|
? array_values(array_filter(array_map('strval', $items), fn(string $item) : bool => trim($item) !== ''))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if ($yearRoot !== '' && $yearModel !== '') {
|
||||||
|
foreach ($folders as $folder) {
|
||||||
|
$folders[] = $yearRoot . '/' . $yearModel . '/' . $folder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique($folders));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int, string> */
|
||||||
|
public function getAudienceGroupsProperty(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Condomini',
|
||||||
|
'Inquilini',
|
||||||
|
'Fornitori',
|
||||||
|
'Collaboratori interni',
|
||||||
|
'Amministratore',
|
||||||
|
'Studio completo',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int, string> */
|
||||||
|
public function getArchivioFisicoTypesProperty(): array
|
||||||
|
{
|
||||||
|
return ['Faldone', 'Scatola', 'Cartella', 'Busta'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMnemonicCodeHintProperty(): ?string
|
||||||
|
{
|
||||||
|
$stabile = $this->resolveActiveStabile();
|
||||||
|
if (! $stabile instanceof Stabile) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = Str::upper((string) ($stabile->denominazione ?? 'STABILE'));
|
||||||
|
$letters = preg_replace('/[^A-Z]/', '', Str::ascii($label)) ?: 'STB';
|
||||||
|
$digits = preg_replace('/\D/', '', $label);
|
||||||
|
$prefix = str_pad(substr($letters, 0, 3), 3, 'X');
|
||||||
|
$suffix = str_pad(substr($digits !== '' ? $digits : (string) $stabile->id, -5), 5, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
return $prefix . $suffix;
|
||||||
|
}
|
||||||
|
|
||||||
private function getDocumentoFormSchema(): array
|
private function getDocumentoFormSchema(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
Select::make('stabile_id')
|
Select::make('stabile_id')
|
||||||
->label('Stabile')
|
->label('Stabile')
|
||||||
->options(fn () => $this->getStabiliOptions())
|
->options(fn() => $this->getStabiliOptions())
|
||||||
->default(fn () => $this->getDefaultStabileId())
|
->default(fn() => $this->getDefaultStabileId())
|
||||||
->searchable()
|
->searchable()
|
||||||
->required(),
|
->required(),
|
||||||
Select::make('categoria')
|
Select::make('categoria')
|
||||||
|
|
@ -251,7 +344,7 @@ private function getDocumentoFormSchema(): array
|
||||||
->nullable(),
|
->nullable(),
|
||||||
Select::make('movimento_contabile_id')
|
Select::make('movimento_contabile_id')
|
||||||
->label('Movimento contabile (opzionale)')
|
->label('Movimento contabile (opzionale)')
|
||||||
->options(fn (callable $get) => $this->getMovimentiOptions((int) ($get('stabile_id') ?: 0)))
|
->options(fn(callable $get) => $this->getMovimentiOptions((int) ($get('stabile_id') ?: 0)))
|
||||||
->searchable()
|
->searchable()
|
||||||
->nullable(),
|
->nullable(),
|
||||||
TextInput::make('faldone')
|
TextInput::make('faldone')
|
||||||
|
|
@ -279,15 +372,15 @@ private function getStabiliOptions(): array
|
||||||
->orderBy('denominazione')
|
->orderBy('denominazione')
|
||||||
->limit(500)
|
->limit(500)
|
||||||
->get(['id', 'denominazione'])
|
->get(['id', 'denominazione'])
|
||||||
->mapWithKeys(fn (Stabile $s) => [(string) $s->id => (($s->denominazione ?: ('Stabile #' . $s->id)))])
|
->mapWithKeys(fn(Stabile $s) => [(string) $s->id => (($s->denominazione ?: ('Stabile #' . $s->id)))])
|
||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowed = StabileContext::accessibleStabili($user);
|
$allowed = StabileContext::accessibleStabili($user);
|
||||||
return $allowed
|
return $allowed
|
||||||
->sortBy(fn (Stabile $s) => $s->denominazione)
|
->sortBy(fn(Stabile $s) => $s->denominazione)
|
||||||
->take(500)
|
->take(500)
|
||||||
->mapWithKeys(fn (Stabile $s) => [(string) $s->id => (($s->denominazione ?: ('Stabile #' . $s->id)))])
|
->mapWithKeys(fn(Stabile $s) => [(string) $s->id => (($s->denominazione ?: ('Stabile #' . $s->id)))])
|
||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,6 +399,23 @@ private function getDefaultStabileId(): ?int
|
||||||
return StabileContext::accessibleStabili($user)->first()?->id;
|
return StabileContext::accessibleStabili($user)->first()?->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeHubTab(string $tab): string
|
||||||
|
{
|
||||||
|
$allowed = ['archivio-digitale', 'template-drive', 'archivio-fisico', 'permessi'];
|
||||||
|
|
||||||
|
return in_array($tab, $allowed, true) ? $tab : 'archivio-digitale';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveActiveStabile(): ?Stabile
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (! $user instanceof User) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StabileContext::getActiveStabile($user);
|
||||||
|
}
|
||||||
|
|
||||||
private function getMovimentiOptions(int $stabileId): array
|
private function getMovimentiOptions(int $stabileId): array
|
||||||
{
|
{
|
||||||
if ($stabileId <= 0) {
|
if ($stabileId <= 0) {
|
||||||
|
|
@ -352,14 +462,14 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
||||||
}
|
}
|
||||||
|
|
||||||
$categoria = (string) ($data['categoria'] ?? 'altro');
|
$categoria = (string) ($data['categoria'] ?? 'altro');
|
||||||
$titolo = (string) ($data['titolo'] ?? '');
|
$titolo = (string) ($data['titolo'] ?? '');
|
||||||
if (trim($titolo) === '') {
|
if (trim($titolo) === '') {
|
||||||
Notification::make()->title('Titolo obbligatorio')->danger()->send();
|
Notification::make()->title('Titolo obbligatorio')->danger()->send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataDocumento = $data['data_documento'] ?? null;
|
$dataDocumento = $data['data_documento'] ?? null;
|
||||||
$year = null;
|
$year = null;
|
||||||
if ($dataDocumento instanceof \DateTimeInterface) {
|
if ($dataDocumento instanceof \DateTimeInterface) {
|
||||||
$year = (int) $dataDocumento->format('Y');
|
$year = (int) $dataDocumento->format('Y');
|
||||||
} elseif (is_string($dataDocumento) && trim($dataDocumento) !== '') {
|
} elseif (is_string($dataDocumento) && trim($dataDocumento) !== '') {
|
||||||
|
|
@ -397,7 +507,7 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
||||||
|
|
||||||
// Rinomina il file su storage in modo ordinato
|
// Rinomina il file su storage in modo ordinato
|
||||||
$safeTitle = Str::slug($titolo);
|
$safeTitle = Str::slug($titolo);
|
||||||
$finalDir = 'documenti/stabili/ID-' . $stabileId . '/' . $year;
|
$finalDir = 'documenti/stabili/ID-' . $stabileId . '/' . $year;
|
||||||
$finalName = $protocollo . '_' . ($safeTitle !== '' ? $safeTitle : 'documento') . '.pdf';
|
$finalName = $protocollo . '_' . ($safeTitle !== '' ? $safeTitle : 'documento') . '.pdf';
|
||||||
$finalPath = $finalDir . '/' . $finalName;
|
$finalPath = $finalDir . '/' . $finalName;
|
||||||
|
|
||||||
|
|
@ -420,7 +530,7 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
$note = '';
|
$note = '';
|
||||||
$faldone = trim((string) ($data['faldone'] ?? ''));
|
$faldone = trim((string) ($data['faldone'] ?? ''));
|
||||||
if ($faldone !== '') {
|
if ($faldone !== '') {
|
||||||
$note = 'Faldone: ' . $faldone;
|
$note = 'Faldone: ' . $faldone;
|
||||||
|
|
@ -429,29 +539,29 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
||||||
$tipologia = $this->mapCategoriaToTipologia($categoria);
|
$tipologia = $this->mapCategoriaToTipologia($categoria);
|
||||||
|
|
||||||
$doc = Documento::query()->create([
|
$doc = Documento::query()->create([
|
||||||
'documentable_type' => null,
|
'documentable_type' => null,
|
||||||
'documentable_id' => null,
|
'documentable_id' => null,
|
||||||
'stabile_id' => $stabileId,
|
'stabile_id' => $stabileId,
|
||||||
'utente_id' => (int) $user->id,
|
'utente_id' => (int) $user->id,
|
||||||
'nome' => $titolo,
|
'nome' => $titolo,
|
||||||
'descrizione' => (string) ($data['descrizione'] ?? ''),
|
'descrizione' => (string) ($data['descrizione'] ?? ''),
|
||||||
'fornitore' => (string) ($data['fornitore'] ?? ''),
|
'fornitore' => (string) ($data['fornitore'] ?? ''),
|
||||||
'tipologia' => $tipologia,
|
'tipologia' => $tipologia,
|
||||||
'tipo_documento' => $categoria,
|
'tipo_documento' => $categoria,
|
||||||
'data_documento' => $dataDocumento,
|
'data_documento' => $dataDocumento,
|
||||||
'data_scadenza' => $data['data_scadenza'] ?? null,
|
'data_scadenza' => $data['data_scadenza'] ?? null,
|
||||||
'importo_collegato' => $data['importo'] ?? null,
|
'importo_collegato' => $data['importo'] ?? null,
|
||||||
'movimento_contabile_id' => $data['movimento_contabile_id'] ?? null,
|
'movimento_contabile_id' => $data['movimento_contabile_id'] ?? null,
|
||||||
'numero_protocollo' => $protocollo,
|
'numero_protocollo' => $protocollo,
|
||||||
'nome_file' => $finalName,
|
'nome_file' => $finalName,
|
||||||
'mime_type' => 'application/pdf',
|
'mime_type' => 'application/pdf',
|
||||||
'path_file' => $finalPath,
|
'path_file' => $finalPath,
|
||||||
'percorso_file' => $finalPath,
|
'percorso_file' => $finalPath,
|
||||||
'estensione' => 'pdf',
|
'estensione' => 'pdf',
|
||||||
'dimensione_file' => $size,
|
'dimensione_file' => $size,
|
||||||
'data_upload' => now(),
|
'data_upload' => now(),
|
||||||
'note' => $note,
|
'note' => $note,
|
||||||
'is_demo' => $isDemo,
|
'is_demo' => $isDemo,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Estrai testo per ricerca (best-effort)
|
// Estrai testo per ricerca (best-effort)
|
||||||
|
|
@ -476,13 +586,13 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$year = 2024;
|
$year = 2024;
|
||||||
$protocollo = $this->generateProtocollo('contratto', $year);
|
$protocollo = $this->generateProtocollo('contratto', $year);
|
||||||
|
|
||||||
$titolo = 'Contratto Manutenzione Ascensori 2024';
|
$titolo = 'Contratto Manutenzione Ascensori 2024';
|
||||||
$descrizione = 'Contratto annuale per manutenzione impianti ascensore con Acme Elevators Srl.';
|
$descrizione = 'Contratto annuale per manutenzione impianti ascensore con Acme Elevators Srl.';
|
||||||
|
|
||||||
$html = '<h1>' . e($titolo) . '</h1>';
|
$html = '<h1>' . e($titolo) . '</h1>';
|
||||||
$html .= '<p><strong>Protocollo:</strong> ' . e($protocollo) . '</p>';
|
$html .= '<p><strong>Protocollo:</strong> ' . e($protocollo) . '</p>';
|
||||||
$html .= '<p>' . e($descrizione) . '</p>';
|
$html .= '<p>' . e($descrizione) . '</p>';
|
||||||
$html .= '<hr>';
|
$html .= '<hr>';
|
||||||
|
|
@ -498,7 +608,7 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
||||||
$dompdf->render();
|
$dompdf->render();
|
||||||
$pdfBytes = $dompdf->output();
|
$pdfBytes = $dompdf->output();
|
||||||
|
|
||||||
$finalDir = 'documenti/stabili/ID-' . $stabileId . '/' . $year;
|
$finalDir = 'documenti/stabili/ID-' . $stabileId . '/' . $year;
|
||||||
$finalName = $protocollo . '_contratto_ascensori_2024.pdf';
|
$finalName = $protocollo . '_contratto_ascensori_2024.pdf';
|
||||||
$finalPath = $finalDir . '/' . $finalName;
|
$finalPath = $finalDir . '/' . $finalName;
|
||||||
|
|
||||||
|
|
@ -511,7 +621,7 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
$note = '';
|
$note = '';
|
||||||
$faldone = trim($faldone);
|
$faldone = trim($faldone);
|
||||||
if ($faldone !== '') {
|
if ($faldone !== '') {
|
||||||
$note = 'Faldone: ' . $faldone;
|
$note = 'Faldone: ' . $faldone;
|
||||||
|
|
@ -520,26 +630,26 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
||||||
}
|
}
|
||||||
|
|
||||||
$doc = Documento::query()->create([
|
$doc = Documento::query()->create([
|
||||||
'stabile_id' => $stabileId,
|
'stabile_id' => $stabileId,
|
||||||
'utente_id' => (int) $user->id,
|
'utente_id' => (int) $user->id,
|
||||||
'nome' => $titolo,
|
'nome' => $titolo,
|
||||||
'descrizione' => $descrizione,
|
'descrizione' => $descrizione,
|
||||||
'fornitore' => 'Acme Elevators Srl',
|
'fornitore' => 'Acme Elevators Srl',
|
||||||
'tipologia' => 'contratto',
|
'tipologia' => 'contratto',
|
||||||
'tipo_documento' => 'contratto',
|
'tipo_documento' => 'contratto',
|
||||||
'data_documento' => '2023-12-15',
|
'data_documento' => '2023-12-15',
|
||||||
'data_scadenza' => '2024-12-31',
|
'data_scadenza' => '2024-12-31',
|
||||||
'importo_collegato' => 2400.00,
|
'importo_collegato' => 2400.00,
|
||||||
'numero_protocollo' => $protocollo,
|
'numero_protocollo' => $protocollo,
|
||||||
'nome_file' => $finalName,
|
'nome_file' => $finalName,
|
||||||
'mime_type' => 'application/pdf',
|
'mime_type' => 'application/pdf',
|
||||||
'path_file' => $finalPath,
|
'path_file' => $finalPath,
|
||||||
'percorso_file' => $finalPath,
|
'percorso_file' => $finalPath,
|
||||||
'estensione' => 'pdf',
|
'estensione' => 'pdf',
|
||||||
'dimensione_file' => $size,
|
'dimensione_file' => $size,
|
||||||
'data_upload' => now(),
|
'data_upload' => now(),
|
||||||
'note' => $note,
|
'note' => $note,
|
||||||
'is_demo' => true,
|
'is_demo' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -558,16 +668,16 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
||||||
private function generateProtocollo(string $categoria, int $year): string
|
private function generateProtocollo(string $categoria, int $year): string
|
||||||
{
|
{
|
||||||
$categoria = strtolower(trim($categoria));
|
$categoria = strtolower(trim($categoria));
|
||||||
$prefissi = [
|
$prefissi = [
|
||||||
'contratto' => 'CONTR',
|
'contratto' => 'CONTR',
|
||||||
'assicurazione' => 'ASSIC',
|
'assicurazione' => 'ASSIC',
|
||||||
'certificato' => 'CERT',
|
'certificato' => 'CERT',
|
||||||
'preventivo' => 'PREV',
|
'preventivo' => 'PREV',
|
||||||
// Importante: i protocolli fattura devono essere per-anno.
|
// Importante: i protocolli fattura devono essere per-anno.
|
||||||
// Usiamo FT come prefisso compatto e compatibile con l'archiviazione.
|
// Usiamo FT come prefisso compatto e compatibile con l'archiviazione.
|
||||||
'fattura' => 'FT',
|
'fattura' => 'FT',
|
||||||
'verbale' => 'VERB',
|
'verbale' => 'VERB',
|
||||||
'altro' => 'DOC',
|
'altro' => 'DOC',
|
||||||
];
|
];
|
||||||
|
|
||||||
$pref = $prefissi[$categoria] ?? 'DOC';
|
$pref = $prefissi[$categoria] ?? 'DOC';
|
||||||
|
|
@ -587,7 +697,7 @@ private function generateProtocollo(string $categoria, int $year): string
|
||||||
$progressivo = 1;
|
$progressivo = 1;
|
||||||
if ($ultimo && is_string($ultimo->numero_protocollo)) {
|
if ($ultimo && is_string($ultimo->numero_protocollo)) {
|
||||||
$parts = explode('-', $ultimo->numero_protocollo);
|
$parts = explode('-', $ultimo->numero_protocollo);
|
||||||
$last = (int) (end($parts) ?: 0);
|
$last = (int) (end($parts) ?: 0);
|
||||||
if ($last > 0) {
|
if ($last > 0) {
|
||||||
$progressivo = $last + 1;
|
$progressivo = $last + 1;
|
||||||
}
|
}
|
||||||
|
|
@ -608,12 +718,12 @@ private function mapCategoriaToTipologia(string $categoria): string
|
||||||
|
|
||||||
// `documenti.tipologia` è un enum: manteniamoci sui valori previsti.
|
// `documenti.tipologia` è un enum: manteniamoci sui valori previsti.
|
||||||
return match ($categoria) {
|
return match ($categoria) {
|
||||||
'contratto' => 'contratto',
|
'contratto' => 'contratto',
|
||||||
'certificato' => 'certificato',
|
'certificato' => 'certificato',
|
||||||
'preventivo' => 'preventivo',
|
'preventivo' => 'preventivo',
|
||||||
'fattura' => 'fattura',
|
'fattura' => 'fattura',
|
||||||
'verbale' => 'verbale',
|
'verbale' => 'verbale',
|
||||||
default => 'altro',
|
default => 'altro',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Filament;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\DocumentoStabile;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class DocumentoStabileDownloadController extends Controller
|
||||||
|
{
|
||||||
|
public function download(Request $request, DocumentoStabile $documentoStabile)
|
||||||
|
{
|
||||||
|
$path = is_string($documentoStabile->percorso_file) ? trim((string) $documentoStabile->percorso_file) : '';
|
||||||
|
if ($path === '') {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedDisk = null;
|
||||||
|
if (Storage::disk('public')->exists($path)) {
|
||||||
|
$selectedDisk = 'public';
|
||||||
|
} elseif (Storage::disk('local')->exists($path)) {
|
||||||
|
$selectedDisk = 'local';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $selectedDisk) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = is_string($documentoStabile->nome_originale) && trim((string) $documentoStabile->nome_originale) !== ''
|
||||||
|
? trim((string) $documentoStabile->nome_originale)
|
||||||
|
: ((is_string($documentoStabile->nome_file) && trim((string) $documentoStabile->nome_file) !== '')
|
||||||
|
? trim((string) $documentoStabile->nome_file)
|
||||||
|
: ('documento-stabile-' . (int) $documentoStabile->id . '.pdf'));
|
||||||
|
|
||||||
|
$filename = str_replace(["\r", "\n", '"'], '', $filename);
|
||||||
|
|
||||||
|
$documentoStabile->incrementaDownload();
|
||||||
|
|
||||||
|
if ($request->boolean('inline')) {
|
||||||
|
return response()->file(Storage::disk($selectedDisk)->path($path), [
|
||||||
|
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Storage::disk($selectedDisk)->download($path, $filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -29,24 +28,24 @@ class DocumentoStabile extends Model
|
||||||
'versione',
|
'versione',
|
||||||
'downloads',
|
'downloads',
|
||||||
'ultimo_accesso',
|
'ultimo_accesso',
|
||||||
'caricato_da'
|
'caricato_da',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'data_scadenza' => 'date',
|
'data_scadenza' => 'date',
|
||||||
'pubblico' => 'boolean',
|
'pubblico' => 'boolean',
|
||||||
'protetto' => 'boolean',
|
'protetto' => 'boolean',
|
||||||
'downloads' => 'integer',
|
'downloads' => 'integer',
|
||||||
'versione' => 'integer',
|
'versione' => 'integer',
|
||||||
'dimensione' => 'integer',
|
'dimensione' => 'integer',
|
||||||
'ultimo_accesso' => 'datetime'
|
'ultimo_accesso' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $dates = [
|
protected $dates = [
|
||||||
'data_scadenza',
|
'data_scadenza',
|
||||||
'ultimo_accesso',
|
'ultimo_accesso',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at'
|
'updated_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,8 +86,8 @@ public function scopePubblici($query)
|
||||||
public function scopeInScadenza($query, $giorni = 30)
|
public function scopeInScadenza($query, $giorni = 30)
|
||||||
{
|
{
|
||||||
return $query->whereNotNull('data_scadenza')
|
return $query->whereNotNull('data_scadenza')
|
||||||
->where('data_scadenza', '<=', now()->addDays($giorni))
|
->where('data_scadenza', '<=', now()->addDays($giorni))
|
||||||
->where('data_scadenza', '>=', now());
|
->where('data_scadenza', '>=', now());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -97,7 +96,7 @@ public function scopeInScadenza($query, $giorni = 30)
|
||||||
public function scopeScaduti($query)
|
public function scopeScaduti($query)
|
||||||
{
|
{
|
||||||
return $query->whereNotNull('data_scadenza')
|
return $query->whereNotNull('data_scadenza')
|
||||||
->where('data_scadenza', '<', now());
|
->where('data_scadenza', '<', now());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,12 +105,14 @@ public function scopeScaduti($query)
|
||||||
public function getDimensioneFormattataAttribute()
|
public function getDimensioneFormattataAttribute()
|
||||||
{
|
{
|
||||||
$bytes = $this->dimensione;
|
$bytes = $this->dimensione;
|
||||||
if ($bytes === 0) return '0 Bytes';
|
if ($bytes === 0) {
|
||||||
|
return '0 Bytes';
|
||||||
$k = 1024;
|
}
|
||||||
|
|
||||||
|
$k = 1024;
|
||||||
$sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
$sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||||
$i = floor(log($bytes) / log($k));
|
$i = floor(log($bytes) / log($k));
|
||||||
|
|
||||||
return number_format($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];
|
return number_format($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +122,7 @@ public function getDimensioneFormattataAttribute()
|
||||||
public function getIconaFileAttribute()
|
public function getIconaFileAttribute()
|
||||||
{
|
{
|
||||||
$ext = strtolower(pathinfo($this->nome_file, PATHINFO_EXTENSION));
|
$ext = strtolower(pathinfo($this->nome_file, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
switch ($ext) {
|
switch ($ext) {
|
||||||
case 'pdf':
|
case 'pdf':
|
||||||
return 'fa-file-pdf text-danger';
|
return 'fa-file-pdf text-danger';
|
||||||
|
|
@ -146,12 +147,12 @@ public function getIconaFileAttribute()
|
||||||
*/
|
*/
|
||||||
public function getStatoScadenzaAttribute()
|
public function getStatoScadenzaAttribute()
|
||||||
{
|
{
|
||||||
if (!$this->data_scadenza) {
|
if (! $this->data_scadenza) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oggi = now()->startOfDay();
|
$oggi = now()->startOfDay();
|
||||||
$scadenza = $this->data_scadenza->startOfDay();
|
$scadenza = $this->data_scadenza->startOfDay();
|
||||||
$diffGiorni = $oggi->diffInDays($scadenza, false);
|
$diffGiorni = $oggi->diffInDays($scadenza, false);
|
||||||
|
|
||||||
if ($diffGiorni < 0) {
|
if ($diffGiorni < 0) {
|
||||||
|
|
@ -170,11 +171,11 @@ public function getStatoScadenzaAttribute()
|
||||||
*/
|
*/
|
||||||
public function getUrlDownloadAttribute()
|
public function getUrlDownloadAttribute()
|
||||||
{
|
{
|
||||||
if (is_string($this->percorso_file) && trim($this->percorso_file) !== '' && Storage::disk('public')->exists($this->percorso_file)) {
|
if (! $this->fileEsiste()) {
|
||||||
return Storage::disk('public')->url($this->percorso_file);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return route('filament.documenti-stabili.download', $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -182,7 +183,13 @@ public function getUrlDownloadAttribute()
|
||||||
*/
|
*/
|
||||||
public function getUrlViewAttribute()
|
public function getUrlViewAttribute()
|
||||||
{
|
{
|
||||||
return $this->url_download;
|
$downloadUrl = $this->url_download;
|
||||||
|
|
||||||
|
if (! is_string($downloadUrl) || trim($downloadUrl) === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $downloadUrl . '?inline=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,7 +207,7 @@ public function incrementaDownload()
|
||||||
public function fileEsiste()
|
public function fileEsiste()
|
||||||
{
|
{
|
||||||
return is_string($this->percorso_file)
|
return is_string($this->percorso_file)
|
||||||
&& trim($this->percorso_file) !== ''
|
&& trim($this->percorso_file) !== ''
|
||||||
&& (Storage::disk('public')->exists($this->percorso_file) || Storage::disk('local')->exists($this->percorso_file));
|
&& (Storage::disk('public')->exists($this->percorso_file) || Storage::disk('local')->exists($this->percorso_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,14 +237,14 @@ public function eliminaFile()
|
||||||
public static function categorie()
|
public static function categorie()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'contratti' => 'Contratti',
|
'contratti' => 'Contratti',
|
||||||
'amministrativi' => 'Documenti Amministrativi',
|
'amministrativi' => 'Documenti Amministrativi',
|
||||||
'tecnici' => 'Documenti Tecnici',
|
'tecnici' => 'Documenti Tecnici',
|
||||||
'catastali' => 'Documenti Catastali',
|
'catastali' => 'Documenti Catastali',
|
||||||
'bancari' => 'Documenti Bancari',
|
'bancari' => 'Documenti Bancari',
|
||||||
'legali' => 'Documenti Legali',
|
'legali' => 'Documenti Legali',
|
||||||
'assemblee' => 'Verbali Assemblee',
|
'assemblee' => 'Verbali Assemblee',
|
||||||
'altri' => 'Altri'
|
'altri' => 'Altri',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,14 +254,14 @@ public static function categorie()
|
||||||
public static function coloriCategorie()
|
public static function coloriCategorie()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'contratti' => 'primary',
|
'contratti' => 'primary',
|
||||||
'amministrativi' => 'info',
|
'amministrativi' => 'info',
|
||||||
'tecnici' => 'warning',
|
'tecnici' => 'warning',
|
||||||
'catastali' => 'success',
|
'catastali' => 'success',
|
||||||
'bancari' => 'secondary',
|
'bancari' => 'secondary',
|
||||||
'legali' => 'danger',
|
'legali' => 'danger',
|
||||||
'assemblee' => 'dark',
|
'assemblee' => 'dark',
|
||||||
'altri' => 'light'
|
'altri' => 'light',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
<div class="-m-6 space-y-3 p-2">
|
<div class="-m-6 space-y-3 p-2">
|
||||||
|
@php
|
||||||
|
$title = isset($title) && is_string($title) && trim($title) !== '' ? trim($title) : 'Documento PDF';
|
||||||
|
$openUrl = isset($openUrl) && is_string($openUrl) && trim($openUrl) !== '' ? trim($openUrl) : $url;
|
||||||
|
@endphp
|
||||||
|
|
||||||
<div class="flex items-center justify-end gap-2 px-4 pt-4">
|
<div class="flex items-center justify-end gap-2 px-4 pt-4">
|
||||||
|
<div class="mr-auto pr-4 text-sm font-semibold text-slate-800">{{ $title }}</div>
|
||||||
<a
|
<a
|
||||||
href="{{ $url }}"
|
href="{{ $openUrl }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
class="inline-flex items-center rounded-md bg-slate-800 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-700"
|
class="inline-flex items-center rounded-md bg-slate-800 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-700"
|
||||||
|
|
|
||||||
|
|
@ -586,6 +586,8 @@
|
||||||
Route::middleware(['role:super-admin|admin|amministratore|collaboratore'])->prefix('admin-filament')->group(function () {
|
Route::middleware(['role:super-admin|admin|amministratore|collaboratore'])->prefix('admin-filament')->group(function () {
|
||||||
Route::get('documenti/{documento}/download', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'download'])
|
Route::get('documenti/{documento}/download', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'download'])
|
||||||
->name('filament.documenti.download');
|
->name('filament.documenti.download');
|
||||||
|
Route::get('documenti-stabili/{documentoStabile}/download', [\App\Http\Controllers\Filament\DocumentoStabileDownloadController::class, 'download'])
|
||||||
|
->name('filament.documenti-stabili.download');
|
||||||
Route::get('documenti/{documento}/etichetta', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'etichetta'])
|
Route::get('documenti/{documento}/etichetta', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'etichetta'])
|
||||||
->name('filament.documenti.etichetta');
|
->name('filament.documenti.etichetta');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user