Harden document archive access and finalize TecnoRepair dedupe
This commit is contained in:
parent
dd825b4450
commit
e462a0b033
|
|
@ -108,6 +108,7 @@ public function handle(TecnoRepairMdbReader $reader, FornitoreProductCatalogServ
|
|||
'schede_lette' => count($schedeRows),
|
||||
'schede_create' => 0,
|
||||
'schede_aggiornate' => 0,
|
||||
'schede_consolidate' => 0,
|
||||
'schede_saltate' => 0,
|
||||
'allegati_importati' => 0,
|
||||
'seriali_allineati' => 0,
|
||||
|
|
@ -118,6 +119,13 @@ public function handle(TecnoRepairMdbReader $reader, FornitoreProductCatalogServ
|
|||
$fornitore->forceFill(['is_tecnorepair_primary' => true])->save();
|
||||
}
|
||||
|
||||
$this->consolidateLegacyDuplicates(
|
||||
amministratoreId: (int) $admin->id,
|
||||
fornitoreId: $fornitore?->id ? (int) $fornitore->id : null,
|
||||
dryRun: $dryRun,
|
||||
stats: $stats,
|
||||
);
|
||||
|
||||
foreach ($schedeRows as $row) {
|
||||
$legacyId = $this->toInt($row['ID'] ?? null);
|
||||
if ($legacyId === null) {
|
||||
|
|
@ -168,10 +176,22 @@ public function handle(TecnoRepairMdbReader $reader, FornitoreProductCatalogServ
|
|||
],
|
||||
];
|
||||
|
||||
$existing = AssistenzaTecnorepairScheda::query()
|
||||
->where('imported_from_path', $mdbPath)
|
||||
->where('legacy_id', $legacyId)
|
||||
->first();
|
||||
$matchingSchede = $this->buildExistingSchedaQuery(
|
||||
amministratoreId: (int) $admin->id,
|
||||
legacyId: $legacyId,
|
||||
fornitoreId: $fornitore?->id ? (int) $fornitore->id : null,
|
||||
)
|
||||
->orderByDesc('fornitore_id')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$existing = $matchingSchede->first();
|
||||
$duplicateSchede = $matchingSchede->slice(1);
|
||||
$duplicateSchedaIds = $duplicateSchede
|
||||
->pluck('id')
|
||||
->filter(fn($value): bool => is_numeric($value) && (int) $value > 0)
|
||||
->map(fn($value): int => (int) $value)
|
||||
->all();
|
||||
|
||||
if ($dryRun) {
|
||||
if ($existing) {
|
||||
|
|
@ -179,16 +199,27 @@ public function handle(TecnoRepairMdbReader $reader, FornitoreProductCatalogServ
|
|||
} else {
|
||||
$stats['schede_create']++;
|
||||
}
|
||||
|
||||
if ($duplicateSchedaIds !== []) {
|
||||
$stats['schede_consolidate'] += count($duplicateSchedaIds);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$scheda = AssistenzaTecnorepairScheda::query()->updateOrCreate(
|
||||
[
|
||||
'imported_from_path' => $mdbPath,
|
||||
'legacy_id' => $legacyId,
|
||||
],
|
||||
$payload,
|
||||
);
|
||||
if ($duplicateSchedaIds !== []) {
|
||||
ProductSerial::query()->whereIn('legacy_scheda_id', $duplicateSchedaIds)->delete();
|
||||
AssistenzaTecnorepairScheda::query()->whereKey($duplicateSchedaIds)->delete();
|
||||
$stats['schede_consolidate'] += count($duplicateSchedaIds);
|
||||
}
|
||||
|
||||
if ($existing instanceof AssistenzaTecnorepairScheda) {
|
||||
$existing->fill($payload);
|
||||
$existing->save();
|
||||
$scheda = $existing;
|
||||
} else {
|
||||
$scheda = AssistenzaTecnorepairScheda::query()->create($payload);
|
||||
}
|
||||
|
||||
if ($existing) {
|
||||
$stats['schede_aggiornate']++;
|
||||
|
|
@ -251,6 +282,7 @@ public function handle(TecnoRepairMdbReader $reader, FornitoreProductCatalogServ
|
|||
['Schede lette', (string) $stats['schede_lette']],
|
||||
['Schede create', (string) $stats['schede_create']],
|
||||
['Schede aggiornate', (string) $stats['schede_aggiornate']],
|
||||
['Schede consolidate', (string) $stats['schede_consolidate']],
|
||||
['Schede saltate', (string) $stats['schede_saltate']],
|
||||
['Allegati importati', (string) $stats['allegati_importati']],
|
||||
['Seriali allineati', (string) $stats['seriali_allineati']],
|
||||
|
|
@ -302,6 +334,93 @@ private function resolveFornitore(Amministratore $admin, string $fornitoreIdOpti
|
|||
->first();
|
||||
}
|
||||
|
||||
private function buildExistingSchedaQuery(int $amministratoreId, int $legacyId, ?int $fornitoreId = null)
|
||||
{
|
||||
return AssistenzaTecnorepairScheda::query()
|
||||
->where('amministratore_id', $amministratoreId)
|
||||
->where('legacy_id', $legacyId)
|
||||
->when(
|
||||
$fornitoreId !== null,
|
||||
fn($query) => $query->where(function ($inner) use ($fornitoreId): void {
|
||||
$inner->where('fornitore_id', $fornitoreId)
|
||||
->orWhereNull('fornitore_id');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private function consolidateLegacyDuplicates(int $amministratoreId, ?int $fornitoreId, bool $dryRun, array &$stats): void
|
||||
{
|
||||
$duplicateLegacyIds = AssistenzaTecnorepairScheda::query()
|
||||
->where('amministratore_id', $amministratoreId)
|
||||
->when(
|
||||
$fornitoreId !== null,
|
||||
fn($query) => $query->where(function ($inner) use ($fornitoreId): void {
|
||||
$inner->where('fornitore_id', $fornitoreId)
|
||||
->orWhereNull('fornitore_id');
|
||||
})
|
||||
)
|
||||
->whereNotNull('legacy_id')
|
||||
->select('legacy_id')
|
||||
->groupBy('legacy_id')
|
||||
->havingRaw('COUNT(*) > 1')
|
||||
->pluck('legacy_id')
|
||||
->filter(fn($value): bool => is_numeric($value) && (int) $value > 0)
|
||||
->map(fn($value): int => (int) $value)
|
||||
->all();
|
||||
|
||||
foreach ($duplicateLegacyIds as $legacyId) {
|
||||
$matchingSchede = $this->buildExistingSchedaQuery(
|
||||
amministratoreId: $amministratoreId,
|
||||
legacyId: $legacyId,
|
||||
fornitoreId: $fornitoreId,
|
||||
)
|
||||
->orderByDesc('fornitore_id')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
if ($matchingSchede->count() <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var AssistenzaTecnorepairScheda $canonical */
|
||||
$canonical = $matchingSchede->first();
|
||||
$duplicateSchede = $matchingSchede->slice(1);
|
||||
|
||||
foreach ($duplicateSchede as $duplicateScheda) {
|
||||
$duplicateId = (int) $duplicateScheda->id;
|
||||
if ($duplicateId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$stats['schede_consolidate']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
AssistenzaTecnorepairAllegato::query()
|
||||
->where('scheda_id', $duplicateId)
|
||||
->update(['scheda_id' => (int) $canonical->id]);
|
||||
|
||||
$serials = ProductSerial::query()
|
||||
->where('legacy_scheda_id', $duplicateId)
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
foreach ($serials as $index => $serial) {
|
||||
if ($index === 0 && ! ProductSerial::query()->where('legacy_scheda_id', (int) $canonical->id)->exists()) {
|
||||
$serial->forceFill(['legacy_scheda_id' => (int) $canonical->id])->save();
|
||||
continue;
|
||||
}
|
||||
|
||||
$serial->delete();
|
||||
}
|
||||
|
||||
$duplicateScheda->delete();
|
||||
$stats['schede_consolidate']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function clean(mixed $value): ?string
|
||||
{
|
||||
if (! is_string($value) && ! is_numeric($value)) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Pages\Contabilita;
|
||||
|
||||
use App\Models\DatiBancari;
|
||||
|
|
@ -14,6 +13,7 @@
|
|||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Concerns\InteractsWithTable;
|
||||
|
|
@ -22,12 +22,16 @@
|
|||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Attributes\Url;
|
||||
use UnitEnum;
|
||||
|
||||
class SaldiContiArchivio extends Page implements HasTable
|
||||
{
|
||||
use InteractsWithTable;
|
||||
|
||||
#[Url(as: 'conto_id')]
|
||||
public ?int $contoId = null;
|
||||
|
||||
protected static ?string $navigationLabel = 'Saldi conti';
|
||||
|
||||
protected static ?string $title = 'Saldi conti';
|
||||
|
|
@ -59,6 +63,8 @@ public static function canAccess(): bool
|
|||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->contoId = is_numeric(request()->query('conto_id')) ? (int) request()->query('conto_id') : null;
|
||||
$this->contoId = $this->contoId && $this->contoId > 0 ? $this->contoId : null;
|
||||
$this->mountInteractsWithTable();
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +82,8 @@ protected function getTableQuery(): Builder
|
|||
|
||||
return SaldoConto::query()
|
||||
->with('conto')
|
||||
->where('stabile_id', $stabileId);
|
||||
->where('stabile_id', $stabileId)
|
||||
->when($this->contoId !== null, fn(Builder $query): Builder => $query->where('conto_id', $this->contoId));
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
|
|
@ -119,6 +126,13 @@ public function table(Table $table): Table
|
|||
->alignEnd()
|
||||
->formatStateUsing(fn($state) => '€ ' . number_format((float) $state, 2, ',', '.')),
|
||||
|
||||
TextColumn::make('is_partenza_contabile')
|
||||
->label('Partenza')
|
||||
->badge()
|
||||
->getStateUsing(fn(SaldoConto $record): string => (bool) ($record->is_partenza_contabile ?? false) ? 'Si' : '—')
|
||||
->color(fn(SaldoConto $record): string => (bool) ($record->is_partenza_contabile ?? false) ? 'info' : 'gray')
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make('tipo_estratto')
|
||||
->label('Tipo')
|
||||
->getStateUsing(fn(SaldoConto $record): string => $this->formatTipoEstrattoLabel($record->tipo_estratto))
|
||||
|
|
@ -156,7 +170,7 @@ public function table(Table $table): Table
|
|||
->label('Conto')
|
||||
->native(false)
|
||||
->searchable()
|
||||
->options(fn(): array => $this->getContiOptions())
|
||||
->options(fn(): array=> $this->getContiOptions())
|
||||
->required(),
|
||||
|
||||
DatePicker::make('data_saldo')->label('Data')->required(),
|
||||
|
|
@ -168,6 +182,9 @@ public function table(Table $table): Table
|
|||
->native(false)
|
||||
->options($this->getTipoEstrattoOptions())
|
||||
->default('estratto_conto'),
|
||||
Toggle::make('is_partenza_contabile')
|
||||
->label('Usa questo saldo come partenza contabile')
|
||||
->default(false),
|
||||
FileUpload::make('documento')
|
||||
->label('Allega estratto')
|
||||
->directory('tmp/banca')
|
||||
|
|
@ -213,20 +230,23 @@ public function table(Table $table): Table
|
|||
? $this->storeSaldoDocumento($stabileId, $conto, $data['documento'] ?? null, $user, $data)
|
||||
: null;
|
||||
|
||||
SaldoConto::query()->create([
|
||||
'stabile_id' => (int) $stabileId,
|
||||
'conto_id' => $contoId,
|
||||
'iban' => $iban,
|
||||
'data_saldo' => $data['data_saldo'] ?? null,
|
||||
'periodo_da' => $data['periodo_da'] ?? null,
|
||||
'periodo_a' => $data['periodo_a'] ?? null,
|
||||
'saldo' => isset($data['saldo']) ? (float) $data['saldo'] : 0.0,
|
||||
'tipo_estratto' => $this->normalizeTipoEstrattoValue($data['tipo_estratto'] ?? null),
|
||||
'note' => isset($data['note']) && is_string($data['note']) ? trim((string) $data['note']) : null,
|
||||
'documento_stabile_id' => $documento?->id,
|
||||
'created_by' => (int) ($user->id ?? 0) ?: null,
|
||||
'updated_by' => (int) ($user->id ?? 0) ?: null,
|
||||
$saldo = SaldoConto::query()->create([
|
||||
'stabile_id' => (int) $stabileId,
|
||||
'conto_id' => $contoId,
|
||||
'iban' => $iban,
|
||||
'data_saldo' => $data['data_saldo'] ?? null,
|
||||
'periodo_da' => $data['periodo_da'] ?? null,
|
||||
'periodo_a' => $data['periodo_a'] ?? null,
|
||||
'saldo' => isset($data['saldo']) ? (float) $data['saldo'] : 0.0,
|
||||
'is_partenza_contabile' => (bool) ($data['is_partenza_contabile'] ?? false),
|
||||
'tipo_estratto' => $this->normalizeTipoEstrattoValue($data['tipo_estratto'] ?? null),
|
||||
'note' => isset($data['note']) && is_string($data['note']) ? trim((string) $data['note']) : null,
|
||||
'documento_stabile_id' => $documento?->id,
|
||||
'created_by' => (int) ($user->id ?? 0) ?: null,
|
||||
'updated_by' => (int) ($user->id ?? 0) ?: null,
|
||||
]);
|
||||
|
||||
$this->syncContabilitaAnchorForSaldo($saldo);
|
||||
}),
|
||||
])
|
||||
->actions([
|
||||
|
|
@ -242,6 +262,8 @@ public function table(Table $table): Table
|
|||
->label('Tipo riferimento')
|
||||
->native(false)
|
||||
->options($this->getTipoEstrattoOptions()),
|
||||
Toggle::make('is_partenza_contabile')
|
||||
->label('Usa questo saldo come partenza contabile'),
|
||||
FileUpload::make('documento')
|
||||
->label('Sostituisci / aggiungi allegato')
|
||||
->directory('tmp/banca')
|
||||
|
|
@ -260,31 +282,35 @@ public function table(Table $table): Table
|
|||
]),
|
||||
Textarea::make('note')->label('Note (opzionale)')->rows(2)->maxLength(255),
|
||||
])
|
||||
->fillForm(fn(SaldoConto $record): array => [
|
||||
'data_saldo' => $record->data_saldo,
|
||||
'periodo_da' => $record->periodo_da,
|
||||
'periodo_a' => $record->periodo_a,
|
||||
'saldo' => (float) $record->saldo,
|
||||
'tipo_estratto' => $record->tipo_estratto,
|
||||
'note' => $record->note,
|
||||
->fillForm(fn(SaldoConto $record): array=> [
|
||||
'data_saldo' => $record->data_saldo,
|
||||
'periodo_da' => $record->periodo_da,
|
||||
'periodo_a' => $record->periodo_a,
|
||||
'saldo' => (float) $record->saldo,
|
||||
'is_partenza_contabile' => (bool) ($record->is_partenza_contabile ?? false),
|
||||
'tipo_estratto' => $record->tipo_estratto,
|
||||
'note' => $record->note,
|
||||
])
|
||||
->action(function (SaldoConto $record, array $data): void {
|
||||
$user = Auth::user();
|
||||
$user = Auth::user();
|
||||
$documento = $record->conto
|
||||
? $this->storeSaldoDocumento((int) $record->stabile_id, $record->conto, $data['documento'] ?? null, $user instanceof User ? $user : null, $data)
|
||||
: null;
|
||||
|
||||
$record->fill([
|
||||
'data_saldo' => $data['data_saldo'] ?? $record->data_saldo,
|
||||
'periodo_da' => $data['periodo_da'] ?? null,
|
||||
'periodo_a' => $data['periodo_a'] ?? null,
|
||||
'saldo' => isset($data['saldo']) ? (float) $data['saldo'] : (float) $record->saldo,
|
||||
'tipo_estratto' => $this->normalizeTipoEstrattoValue($data['tipo_estratto'] ?? null),
|
||||
'note' => isset($data['note']) && is_string($data['note']) ? trim((string) $data['note']) : null,
|
||||
'documento_stabile_id' => $documento?->id ?: $record->documento_stabile_id,
|
||||
'updated_by' => $user instanceof User ? ((int) $user->id ?: null) : null,
|
||||
'data_saldo' => $data['data_saldo'] ?? $record->data_saldo,
|
||||
'periodo_da' => $data['periodo_da'] ?? null,
|
||||
'periodo_a' => $data['periodo_a'] ?? null,
|
||||
'saldo' => isset($data['saldo']) ? (float) $data['saldo'] : (float) $record->saldo,
|
||||
'is_partenza_contabile' => (bool) ($data['is_partenza_contabile'] ?? false),
|
||||
'tipo_estratto' => $this->normalizeTipoEstrattoValue($data['tipo_estratto'] ?? null),
|
||||
'note' => isset($data['note']) && is_string($data['note']) ? trim((string) $data['note']) : null,
|
||||
'documento_stabile_id' => $documento?->id ?: $record->documento_stabile_id,
|
||||
'updated_by' => $user instanceof User ? ((int) $user->id ?: null): null,
|
||||
]);
|
||||
$record->save();
|
||||
|
||||
$this->syncContabilitaAnchorForSaldo($record);
|
||||
}),
|
||||
|
||||
Action::make('delete')
|
||||
|
|
@ -321,7 +347,7 @@ protected function getContiOptions(): array
|
|||
->orderBy('id')
|
||||
->get(['id', 'denominazione_banca', 'iban'])
|
||||
->mapWithKeys(function (DatiBancari $r): array {
|
||||
$iban = is_string($r->iban) ? trim((string) $r->iban) : '';
|
||||
$iban = is_string($r->iban) ? trim((string) $r->iban) : '';
|
||||
$label = trim((string) ($r->denominazione_banca ?: 'Conto') . ($iban !== '' ? (' · ' . $iban) : ''));
|
||||
return [(int) $r->id => $label];
|
||||
})
|
||||
|
|
@ -331,10 +357,10 @@ protected function getContiOptions(): array
|
|||
protected function getTipoEstrattoOptions(): array
|
||||
{
|
||||
return [
|
||||
'estratto_conto' => 'Estratto conto',
|
||||
'saldo_banca' => 'Saldo banca',
|
||||
'estratto_conto' => 'Estratto conto',
|
||||
'saldo_banca' => 'Saldo banca',
|
||||
'riconciliazione' => 'Riconciliazione',
|
||||
'altro' => 'Altro',
|
||||
'altro' => 'Altro',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +387,7 @@ protected function formatTipoEstrattoLabel(?string $value): string
|
|||
protected function formatPeriodoEstrattoLabel(mixed $from, mixed $to): string
|
||||
{
|
||||
$fromDate = $from ? \Illuminate\Support\Carbon::parse((string) $from) : null;
|
||||
$toDate = $to ? \Illuminate\Support\Carbon::parse((string) $to) : null;
|
||||
$toDate = $to ? \Illuminate\Support\Carbon::parse((string) $to) : null;
|
||||
|
||||
if ($fromDate && $toDate) {
|
||||
return $fromDate->format('d/m/Y') . ' - ' . $toDate->format('d/m/Y');
|
||||
|
|
@ -383,9 +409,9 @@ protected function storeSaldoDocumento(int $stabileId, DatiBancari $conto, mixed
|
|||
return null;
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$extension = $extension !== '' ? $extension : 'bin';
|
||||
$filename = 'estratto-conto-' . $stabileId . '-' . (int) $conto->id . '-' . now()->format('YmdHis') . '.' . $extension;
|
||||
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
|
||||
$extension = $extension !== '' ? $extension : 'bin';
|
||||
$filename = 'estratto-conto-' . $stabileId . '-' . (int) $conto->id . '-' . now()->format('YmdHis') . '.' . $extension;
|
||||
$publicPath = 'documenti/stabili/' . $stabileId . '/bancari/' . $filename;
|
||||
|
||||
\Illuminate\Support\Facades\Storage::disk('public')->put($publicPath, \Illuminate\Support\Facades\Storage::disk('local')->get($path));
|
||||
|
|
@ -396,15 +422,28 @@ protected function storeSaldoDocumento(int $stabileId, DatiBancari $conto, mixed
|
|||
}
|
||||
|
||||
return DocumentoStabile::query()->create([
|
||||
'stabile_id' => $stabileId,
|
||||
'nome_file' => $filename,
|
||||
'stabile_id' => $stabileId,
|
||||
'nome_file' => $filename,
|
||||
'nome_originale' => $filename,
|
||||
'percorso_file' => $publicPath,
|
||||
'categoria' => 'bancari',
|
||||
'tipo_mime' => \Illuminate\Support\Facades\Storage::disk('public')->mimeType($publicPath),
|
||||
'dimensione' => \Illuminate\Support\Facades\Storage::disk('public')->size($publicPath),
|
||||
'descrizione' => 'Estratto conto ' . trim((string) ($conto->denominazione_banca ?: 'Conto')) . ' · ' . $this->formatPeriodoEstrattoLabel($data['periodo_da'] ?? null, $data['periodo_a'] ?? null),
|
||||
'caricato_da' => (int) $user->id,
|
||||
'percorso_file' => $publicPath,
|
||||
'categoria' => 'bancari',
|
||||
'tipo_mime' => \Illuminate\Support\Facades\Storage::disk('public')->mimeType($publicPath),
|
||||
'dimensione' => \Illuminate\Support\Facades\Storage::disk('public')->size($publicPath),
|
||||
'descrizione' => 'Estratto conto ' . trim((string) ($conto->denominazione_banca ?: 'Conto')) . ' · ' . $this->formatPeriodoEstrattoLabel($data['periodo_da'] ?? null, $data['periodo_a'] ?? null),
|
||||
'caricato_da' => (int) $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function syncContabilitaAnchorForSaldo(SaldoConto $saldo): void
|
||||
{
|
||||
if (! ((bool) ($saldo->is_partenza_contabile ?? false))) {
|
||||
return;
|
||||
}
|
||||
|
||||
SaldoConto::query()
|
||||
->where('stabile_id', (int) $saldo->stabile_id)
|
||||
->where('conto_id', (int) $saldo->conto_id)
|
||||
->whereKeyNot($saldo->id)
|
||||
->update(['is_partenza_contabile' => false]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
namespace App\Filament\Pages\Strumenti;
|
||||
|
||||
use App\Models\Documento;
|
||||
use App\Models\DocumentoArchivioCartella;
|
||||
use App\Models\MovimentoContabile;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\User;
|
||||
use App\Services\Documenti\DocumentoArchivioVisibilityService;
|
||||
use App\Services\Documenti\PdfTextExtractionService;
|
||||
use App\Support\StabileContext;
|
||||
use BackedEnum;
|
||||
|
|
@ -53,6 +55,8 @@ class DocumentiArchivio extends Page implements HasTable
|
|||
|
||||
protected string $view = 'filament.pages.strumenti.documenti-archivio';
|
||||
|
||||
private ?DocumentoArchivioVisibilityService $visibilityService = null;
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
|
@ -100,6 +104,15 @@ protected function getHeaderActions(): array
|
|||
->action(function (array $data): void {
|
||||
$this->createDocumentoDemoContrattoAscensori((int) $data['stabile_id'], (string) ($data['faldone'] ?? ''));
|
||||
}),
|
||||
|
||||
Action::make('nuova_cartella_archivio')
|
||||
->label('Nuova cartella archivio')
|
||||
->icon('heroicon-o-folder-plus')
|
||||
->color('gray')
|
||||
->form($this->getArchivioFolderFormSchema())
|
||||
->action(function (array $data): void {
|
||||
$this->createArchivioFolder($data);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -110,27 +123,8 @@ protected function getTableQuery(): Builder
|
|||
return Documento::query()->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
$query = Documento::query();
|
||||
|
||||
$activeStabile = StabileContext::getActiveStabile($user);
|
||||
if ($activeStabile instanceof Stabile) {
|
||||
return $query->where('stabile_id', (int) $activeStabile->id);
|
||||
}
|
||||
|
||||
if ($user->hasAnyRole(['super-admin', 'admin'])) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$allowedIds = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($v) => (int) $v)
|
||||
->values();
|
||||
|
||||
if ($allowedIds->isEmpty()) {
|
||||
return Documento::query()->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
return $query->whereIn('stabile_id', $allowedIds);
|
||||
return $this->getVisibilityService()
|
||||
->scopeVisibleDocumenti(Documento::query()->with('cartellaArchivio'), $user);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
|
|
@ -161,6 +155,10 @@ public function table(Table $table): Table
|
|||
->searchable()
|
||||
->limit(35)
|
||||
->toggleable(),
|
||||
TextColumn::make('cartellaArchivio.nome')
|
||||
->label('Cartella')
|
||||
->toggleable()
|
||||
->wrap(),
|
||||
TextColumn::make('contenuto_ocr')
|
||||
->label('Testo (OCR)')
|
||||
->limit(50)
|
||||
|
|
@ -290,6 +288,44 @@ public function getArchivioFisicoTypesProperty(): array
|
|||
return ['Faldone', 'Scatola', 'Cartella', 'Busta'];
|
||||
}
|
||||
|
||||
public function getVisibleFolderRowsProperty(): array
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user instanceof User) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stabileId = $this->resolveActiveStabile()?->id;
|
||||
|
||||
return $this->getVisibilityService()
|
||||
->scopeVisibleCartelle(DocumentoArchivioCartella::query()->with('parent', 'stabile'), $user, $stabileId ? (int) $stabileId : null)
|
||||
->orderBy('sort_order')
|
||||
->orderBy('nome')
|
||||
->get()
|
||||
->map(function (DocumentoArchivioCartella $folder): array {
|
||||
$roles = collect($folder->visibility_roles ?? [])->map(fn($value): string => (string) $value)->filter()->values()->all();
|
||||
$groups = collect($folder->visibility_groups ?? [])->map(fn($value): string => (string) $value)->filter()->values()->all();
|
||||
$users = collect($folder->allowed_user_ids ?? [])->filter(fn($value): bool => is_numeric($value) && (int) $value > 0)->map(fn($value): int => (int) $value)->all();
|
||||
|
||||
$userNames = $users === []
|
||||
? []
|
||||
: User::query()->whereIn('id', $users)->orderBy('name')->pluck('name')->map(fn($value): string => (string) $value)->all();
|
||||
|
||||
return [
|
||||
'id' => (int) $folder->id,
|
||||
'nome' => (string) $folder->nome,
|
||||
'path' => (string) ($folder->display_path ?: $folder->nome),
|
||||
'stabile' => (string) ($folder->stabile?->denominazione ?: ('Stabile #' . (int) $folder->stabile_id)),
|
||||
'scope' => (string) ($folder->visibility_scope ?: 'interno'),
|
||||
'roles' => $roles,
|
||||
'groups' => $groups,
|
||||
'users' => $userNames,
|
||||
'documents_count' => (int) $folder->documenti()->count(),
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
public function getMnemonicCodeHintProperty(): ?string
|
||||
{
|
||||
$stabile = $this->resolveActiveStabile();
|
||||
|
|
@ -314,7 +350,13 @@ private function getDocumentoFormSchema(): array
|
|||
->options(fn() => $this->getStabiliOptions())
|
||||
->default(fn() => $this->getDefaultStabileId())
|
||||
->searchable()
|
||||
->live()
|
||||
->required(),
|
||||
Select::make('cartella_archivio_id')
|
||||
->label('Cartella archivio')
|
||||
->options(fn(callable $get): array => $this->getArchivioFolderOptions((int) ($get('stabile_id') ?: 0)))
|
||||
->searchable()
|
||||
->nullable(),
|
||||
Select::make('categoria')
|
||||
->label('Categoria protocollo')
|
||||
->options($this->getCategorieOptions())
|
||||
|
|
@ -352,6 +394,22 @@ private function getDocumentoFormSchema(): array
|
|||
->placeholder('Es. CONTR-2024')
|
||||
->maxLength(50)
|
||||
->nullable(),
|
||||
Select::make('visibility_scope')
|
||||
->label('Visibilità')
|
||||
->options($this->getVisibilityScopeOptions())
|
||||
->default('interno')
|
||||
->native(false)
|
||||
->required(),
|
||||
Select::make('visibility_roles')
|
||||
->label('Ruoli ammessi')
|
||||
->options($this->getVisibilityRoleOptions())
|
||||
->multiple()
|
||||
->native(false),
|
||||
Select::make('visibility_groups')
|
||||
->label('Gruppi audience')
|
||||
->options(array_combine($this->audienceGroups, $this->audienceGroups))
|
||||
->multiple()
|
||||
->native(false),
|
||||
FileUpload::make('pdf')
|
||||
->label('PDF')
|
||||
->disk('local')
|
||||
|
|
@ -360,6 +418,55 @@ private function getDocumentoFormSchema(): array
|
|||
];
|
||||
}
|
||||
|
||||
private function getArchivioFolderFormSchema(): array
|
||||
{
|
||||
return [
|
||||
Select::make('stabile_id')
|
||||
->label('Stabile')
|
||||
->options(fn(): array => $this->getStabiliOptions())
|
||||
->default(fn(): ?int => $this->getDefaultStabileId())
|
||||
->searchable()
|
||||
->live()
|
||||
->required(),
|
||||
Select::make('parent_id')
|
||||
->label('Cartella padre')
|
||||
->options(fn(callable $get): array => $this->getArchivioFolderOptions((int) ($get('stabile_id') ?: 0)))
|
||||
->searchable()
|
||||
->nullable(),
|
||||
TextInput::make('nome')
|
||||
->label('Nome cartella')
|
||||
->required()
|
||||
->maxLength(160),
|
||||
TextInput::make('codice')
|
||||
->label('Codice cartella')
|
||||
->maxLength(80),
|
||||
Select::make('visibility_scope')
|
||||
->label('Visibilità')
|
||||
->options($this->getVisibilityScopeOptions())
|
||||
->default('interno')
|
||||
->native(false)
|
||||
->required(),
|
||||
Select::make('visibility_roles')
|
||||
->label('Ruoli ammessi')
|
||||
->options($this->getVisibilityRoleOptions())
|
||||
->multiple()
|
||||
->native(false),
|
||||
Select::make('visibility_groups')
|
||||
->label('Gruppi audience')
|
||||
->options(array_combine($this->audienceGroups, $this->audienceGroups))
|
||||
->multiple()
|
||||
->native(false),
|
||||
Select::make('allowed_user_ids')
|
||||
->label('Utenti specifici')
|
||||
->options($this->getCollaboratorUserOptions())
|
||||
->multiple()
|
||||
->native(false),
|
||||
Textarea::make('note')
|
||||
->label('Note cartella')
|
||||
->rows(3),
|
||||
];
|
||||
}
|
||||
|
||||
private function getStabiliOptions(): array
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
|
@ -399,6 +506,68 @@ private function getDefaultStabileId(): ?int
|
|||
return StabileContext::accessibleStabili($user)->first()?->id;
|
||||
}
|
||||
|
||||
private function getVisibilityService(): DocumentoArchivioVisibilityService
|
||||
{
|
||||
return $this->visibilityService ??= app(DocumentoArchivioVisibilityService::class);
|
||||
}
|
||||
|
||||
private function getVisibilityScopeOptions(): array
|
||||
{
|
||||
return [
|
||||
'interno' => 'Interno studio',
|
||||
'studio' => 'Studio completo',
|
||||
'restricted' => 'Limitato a ruoli / utenti',
|
||||
];
|
||||
}
|
||||
|
||||
private function getVisibilityRoleOptions(): array
|
||||
{
|
||||
return [
|
||||
'super-admin' => 'Super admin',
|
||||
'admin' => 'Admin',
|
||||
'amministratore' => 'Amministratore',
|
||||
'collaboratore' => 'Collaboratore',
|
||||
];
|
||||
}
|
||||
|
||||
private function getCollaboratorUserOptions(): array
|
||||
{
|
||||
return User::query()
|
||||
->role(['super-admin', 'admin', 'amministratore', 'collaboratore'])
|
||||
->orderBy('name')
|
||||
->limit(300)
|
||||
->get(['id', 'name', 'email'])
|
||||
->mapWithKeys(function (User $user): array {
|
||||
$label = trim((string) $user->name);
|
||||
if (trim((string) $user->email) !== '') {
|
||||
$label .= ' <' . trim((string) $user->email) . '>';
|
||||
}
|
||||
|
||||
return [(string) $user->id => $label !== '' ? $label : ('Utente #' . (int) $user->id)];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
private function getArchivioFolderOptions(int $stabileId): array
|
||||
{
|
||||
if ($stabileId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return DocumentoArchivioCartella::query()
|
||||
->where('stabile_id', $stabileId)
|
||||
->where('is_active', true)
|
||||
->orderBy('sort_order')
|
||||
->orderBy('nome')
|
||||
->get(['id', 'nome', 'percorso_logico'])
|
||||
->mapWithKeys(function (DocumentoArchivioCartella $folder): array {
|
||||
$label = trim((string) ($folder->percorso_logico ?: $folder->nome));
|
||||
|
||||
return [(string) $folder->id => $label !== '' ? $label : ('Cartella #' . (int) $folder->id)];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
private function normalizeHubTab(string $tab): string
|
||||
{
|
||||
$allowed = ['archivio-digitale', 'template-drive', 'archivio-fisico', 'permessi'];
|
||||
|
|
@ -447,6 +616,66 @@ private function getMovimentiOptions(int $stabileId): array
|
|||
->all();
|
||||
}
|
||||
|
||||
private function createArchivioFolder(array $data): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user instanceof User) {
|
||||
Notification::make()->title('Utente non valido')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$stabileId = isset($data['stabile_id']) && is_numeric($data['stabile_id']) ? (int) $data['stabile_id'] : 0;
|
||||
if ($stabileId <= 0) {
|
||||
Notification::make()->title('Stabile obbligatorio')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$allowedIds = StabileContext::accessibleStabili($user)->pluck('id')->map(fn($value): int => (int) $value)->all();
|
||||
if (! in_array($stabileId, $allowedIds, true) && ! $user->hasAnyRole(['super-admin', 'admin'])) {
|
||||
Notification::make()->title('Stabile non accessibile')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$name = trim((string) ($data['nome'] ?? ''));
|
||||
if ($name === '') {
|
||||
Notification::make()->title('Nome cartella obbligatorio')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$parentId = isset($data['parent_id']) && is_numeric($data['parent_id']) ? (int) $data['parent_id'] : null;
|
||||
$parent = $parentId
|
||||
? DocumentoArchivioCartella::query()->where('stabile_id', $stabileId)->find($parentId)
|
||||
: null;
|
||||
|
||||
$slug = Str::slug($name);
|
||||
if ($slug === '') {
|
||||
$slug = 'cartella-' . now()->format('YmdHis');
|
||||
}
|
||||
|
||||
$logicalPath = collect([
|
||||
$parent?->percorso_logico,
|
||||
$name,
|
||||
])->filter(fn($value): bool => is_string($value) && trim($value) !== '')->implode('/');
|
||||
|
||||
DocumentoArchivioCartella::query()->create([
|
||||
'stabile_id' => $stabileId,
|
||||
'parent_id' => $parent?->id,
|
||||
'nome' => $name,
|
||||
'slug' => $slug,
|
||||
'codice' => trim((string) ($data['codice'] ?? '')) ?: null,
|
||||
'percorso_logico' => $logicalPath !== '' ? $logicalPath : $name,
|
||||
'visibility_scope' => (string) ($data['visibility_scope'] ?? 'interno'),
|
||||
'visibility_roles' => $this->normalizeStringArray($data['visibility_roles'] ?? []),
|
||||
'visibility_groups' => $this->normalizeStringArray($data['visibility_groups'] ?? []),
|
||||
'allowed_user_ids' => $this->normalizeIntArray($data['allowed_user_ids'] ?? []),
|
||||
'note' => trim((string) ($data['note'] ?? '')) ?: null,
|
||||
'created_by' => (int) $user->id,
|
||||
'updated_by' => (int) $user->id,
|
||||
]);
|
||||
|
||||
Notification::make()->title('Cartella archivio creata')->success()->send();
|
||||
}
|
||||
|
||||
private function createDocumentoFromForm(array $data, bool $isDemo): void
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
|
@ -542,6 +771,7 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
|||
'documentable_type' => null,
|
||||
'documentable_id' => null,
|
||||
'stabile_id' => $stabileId,
|
||||
'cartella_archivio_id' => $this->resolveFolderIdForDocument($stabileId, $data['cartella_archivio_id'] ?? null),
|
||||
'utente_id' => (int) $user->id,
|
||||
'nome' => $titolo,
|
||||
'descrizione' => (string) ($data['descrizione'] ?? ''),
|
||||
|
|
@ -559,6 +789,9 @@ private function createDocumentoFromForm(array $data, bool $isDemo): void
|
|||
'percorso_file' => $finalPath,
|
||||
'estensione' => 'pdf',
|
||||
'dimensione_file' => $size,
|
||||
'visibility_scope' => (string) ($data['visibility_scope'] ?? 'interno'),
|
||||
'visibility_roles' => $this->normalizeStringArray($data['visibility_roles'] ?? []),
|
||||
'visibility_groups' => $this->normalizeStringArray($data['visibility_groups'] ?? []),
|
||||
'data_upload' => now(),
|
||||
'note' => $note,
|
||||
'is_demo' => $isDemo,
|
||||
|
|
@ -647,6 +880,7 @@ private function createDocumentoDemoContrattoAscensori(int $stabileId, string $f
|
|||
'percorso_file' => $finalPath,
|
||||
'estensione' => 'pdf',
|
||||
'dimensione_file' => $size,
|
||||
'visibility_scope' => 'interno',
|
||||
'data_upload' => now(),
|
||||
'note' => $note,
|
||||
'is_demo' => true,
|
||||
|
|
@ -726,4 +960,46 @@ private function mapCategoriaToTipologia(string $categoria): string
|
|||
default => 'altro',
|
||||
};
|
||||
}
|
||||
|
||||
private function resolveFolderIdForDocument(int $stabileId, mixed $folderId): ?int
|
||||
{
|
||||
if (! is_numeric($folderId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$folder = DocumentoArchivioCartella::query()
|
||||
->where('stabile_id', $stabileId)
|
||||
->whereKey((int) $folderId)
|
||||
->first(['id']);
|
||||
|
||||
return $folder ? (int) $folder->id : null;
|
||||
}
|
||||
|
||||
private function normalizeStringArray(mixed $values): array
|
||||
{
|
||||
if (! is_array($values)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return collect($values)
|
||||
->map(fn($value): string => trim((string) $value))
|
||||
->filter(fn(string $value): bool => $value !== '')
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function normalizeIntArray(mixed $values): array
|
||||
{
|
||||
if (! is_array($values)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return collect($values)
|
||||
->filter(fn($value): bool => is_numeric($value) && (int) $value > 0)
|
||||
->map(fn($value): int => (int) $value)
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Documento;
|
||||
use App\Models\User;
|
||||
use App\Services\Documenti\DocumentoArchivioVisibilityService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
|
@ -13,8 +14,13 @@
|
|||
|
||||
class DocumentiPrintController extends Controller
|
||||
{
|
||||
public function download(Request $request, Documento $documento)
|
||||
public function download(Request $request, Documento $documento, DocumentoArchivioVisibilityService $visibilityService)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user instanceof User || ! $visibilityService->canAccessDocumento($user, $documento)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$pathCandidates = [];
|
||||
if (is_string($documento->percorso_file) && trim($documento->percorso_file) !== '') {
|
||||
$pathCandidates[] = trim($documento->percorso_file);
|
||||
|
|
@ -57,8 +63,13 @@ public function download(Request $request, Documento $documento)
|
|||
return Storage::disk($selectedDisk)->download($selectedPath, $filename);
|
||||
}
|
||||
|
||||
public function etichetta(Request $request, Documento $documento)
|
||||
public function etichetta(Request $request, Documento $documento, DocumentoArchivioVisibilityService $visibilityService)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user instanceof User || ! $visibilityService->canAccessDocumento($user, $documento)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$format = $request->query('format', '11354');
|
||||
$format = in_array($format, ['11354', '99014'], true) ? $format : '11354';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,21 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\DocumentoStabile;
|
||||
use App\Models\User;
|
||||
use App\Services\Documenti\DocumentoArchivioVisibilityService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DocumentoStabileDownloadController extends Controller
|
||||
{
|
||||
public function download(Request $request, DocumentoStabile $documentoStabile)
|
||||
public function download(Request $request, DocumentoStabile $documentoStabile, DocumentoArchivioVisibilityService $visibilityService)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if (! $user instanceof User || ! $visibilityService->canAccessDocumentoStabile($user, $documentoStabile)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$path = is_string($documentoStabile->percorso_file) ? trim((string) $documentoStabile->percorso_file) : '';
|
||||
if ($path === '') {
|
||||
abort(404);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Condomini;
|
||||
|
||||
use App\Filament\Pages\Contabilita\CasseBancheMovimenti;
|
||||
|
|
@ -97,13 +96,13 @@ public function table(Table $table): Table
|
|||
|
||||
TextColumn::make('saldo_ufficiale')
|
||||
->label('Saldo ufficiale')
|
||||
->stateUsing(fn(DatiBancari $record): ?float => $this->resolveLatestSaldoSnapshotValue($record))
|
||||
->getStateUsing(fn(DatiBancari $record): ?float => $this->resolveLatestSaldoSnapshotValue($record))
|
||||
->money('EUR')
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make('ultimo_estratto')
|
||||
->label('Ultimo estratto')
|
||||
->stateUsing(fn(DatiBancari $record): string => $this->resolveLatestSaldoSnapshotLabel($record))
|
||||
->getStateUsing(fn(DatiBancari $record): string => $this->resolveLatestSaldoSnapshotLabel($record))
|
||||
->wrap()
|
||||
->toggleable(),
|
||||
|
||||
|
|
@ -131,8 +130,8 @@ public function table(Table $table): Table
|
|||
return;
|
||||
}
|
||||
|
||||
$amm = $stabile->amministratore;
|
||||
$adminCode = $amm ? (string) ($amm->codice_univoco ?: $amm->codice_amministratore ?: '') : '';
|
||||
$amm = $stabile->amministratore;
|
||||
$adminCode = $amm ? (string) ($amm->codice_univoco ?: $amm->codice_amministratore ?: ''): '';
|
||||
$adminCode = trim($adminCode);
|
||||
if ($adminCode === '') {
|
||||
$adminCode = $amm ? ('ID' . $amm->id) : 'ID0';
|
||||
|
|
@ -174,12 +173,12 @@ public function table(Table $table): Table
|
|||
->form($this->formSchema())
|
||||
->action(function (array $data): void {
|
||||
$user = Auth::user();
|
||||
$uid = $user instanceof User ? (int) $user->id : null;
|
||||
$uid = $user instanceof User ? (int) $user->id : null;
|
||||
|
||||
$payload = $this->normalizePayload($data);
|
||||
$payload = $this->normalizePayload($data);
|
||||
$payload['stabile_id'] = (int) $this->stabileId;
|
||||
if ($uid) {
|
||||
$payload['creato_da'] = $uid;
|
||||
$payload['creato_da'] = $uid;
|
||||
$payload['modificato_da'] = $uid;
|
||||
}
|
||||
|
||||
|
|
@ -197,17 +196,13 @@ public function table(Table $table): Table
|
|||
->icon('heroicon-o-receipt-percent')
|
||||
->url(function (DatiBancari $record): string {
|
||||
$base = CasseBancheMovimenti::getUrl(panel: 'admin-filament');
|
||||
$iban = is_string($record->iban ?? null) ? trim((string) $record->iban) : '';
|
||||
if ($iban === '') {
|
||||
$contoId = (int) ($record->id ?? 0);
|
||||
if ($contoId <= 0) {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return $base . '?' . http_build_query([
|
||||
'tableFilters' => [
|
||||
'conto' => [
|
||||
'ibans' => [$iban],
|
||||
],
|
||||
],
|
||||
'conto_id' => $contoId,
|
||||
]);
|
||||
})
|
||||
->openUrlInNewTab(),
|
||||
|
|
@ -215,7 +210,7 @@ public function table(Table $table): Table
|
|||
Action::make('saldi')
|
||||
->label('Saldi')
|
||||
->icon('heroicon-o-scale')
|
||||
->url(fn(): string => SaldiContiArchivio::getUrl(panel: 'admin-filament'))
|
||||
->url(fn(DatiBancari $record): string => SaldiContiArchivio::getUrl(['conto_id' => (int) $record->id], panel: 'admin-filament'))
|
||||
->openUrlInNewTab(),
|
||||
|
||||
Action::make('contatto')
|
||||
|
|
@ -230,30 +225,30 @@ public function table(Table $table): Table
|
|||
->label('Modifica')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->modalHeading('Modifica conto')
|
||||
->fillForm(fn(DatiBancari $record): array => [
|
||||
->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,
|
||||
'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,
|
||||
'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;
|
||||
$uid = $user instanceof User ? (int) $user->id : null;
|
||||
|
||||
$payload = $this->normalizePayload($data);
|
||||
if ($uid) {
|
||||
|
|
@ -306,8 +301,8 @@ private function formSchema(): array
|
|||
Select::make('tipo_conto')
|
||||
->label('Tipo conto')
|
||||
->options([
|
||||
'corrente' => 'Corrente',
|
||||
'deposito' => 'Deposito',
|
||||
'corrente' => 'Corrente',
|
||||
'deposito' => 'Deposito',
|
||||
'risparmio' => 'Risparmio',
|
||||
])
|
||||
->default('corrente')
|
||||
|
|
@ -316,9 +311,9 @@ private function formSchema(): array
|
|||
Select::make('stato_conto')
|
||||
->label('Stato')
|
||||
->options([
|
||||
'attivo' => 'Attivo',
|
||||
'attivo' => 'Attivo',
|
||||
'sospeso' => 'Sospeso',
|
||||
'chiuso' => 'Chiuso',
|
||||
'chiuso' => 'Chiuso',
|
||||
])
|
||||
->default('attivo')
|
||||
->required(),
|
||||
|
|
@ -420,23 +415,23 @@ 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,
|
||||
'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,
|
||||
'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,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -475,15 +470,15 @@ private function resolveLatestSaldoSnapshotLabel(DatiBancari $record): string
|
|||
$parts = [];
|
||||
if ($snapshot->tipo_estratto) {
|
||||
$parts[] = match ($snapshot->tipo_estratto) {
|
||||
'estratto_conto' => 'Estratto conto',
|
||||
'saldo_banca' => 'Saldo banca',
|
||||
'estratto_conto' => 'Estratto conto',
|
||||
'saldo_banca' => 'Saldo banca',
|
||||
'riconciliazione' => 'Riconciliazione',
|
||||
default => 'Altro',
|
||||
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') ?: '...';
|
||||
$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';
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class Documento extends Model
|
|||
|
||||
// Nuovi campi per sistema avanzato
|
||||
'stabile_id',
|
||||
'cartella_archivio_id',
|
||||
'unita_id',
|
||||
'utente_id',
|
||||
'nome',
|
||||
|
|
@ -106,6 +107,11 @@ public function stabile(): BelongsTo
|
|||
return $this->belongsTo(Stabile::class);
|
||||
}
|
||||
|
||||
public function cartellaArchivio(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(DocumentoArchivioCartella::class, 'cartella_archivio_id');
|
||||
}
|
||||
|
||||
public function unita(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UnitaImmobiliare::class);
|
||||
|
|
|
|||
73
app/Models/DocumentoArchivioCartella.php
Normal file
73
app/Models/DocumentoArchivioCartella.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class DocumentoArchivioCartella extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'documento_archivio_cartelle';
|
||||
|
||||
protected $fillable = [
|
||||
'stabile_id',
|
||||
'parent_id',
|
||||
'nome',
|
||||
'slug',
|
||||
'codice',
|
||||
'percorso_logico',
|
||||
'visibility_scope',
|
||||
'visibility_roles',
|
||||
'visibility_groups',
|
||||
'allowed_user_ids',
|
||||
'note',
|
||||
'sort_order',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'visibility_roles' => 'array',
|
||||
'visibility_groups' => 'array',
|
||||
'allowed_user_ids' => 'array',
|
||||
'sort_order' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function stabile(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Stabile::class, 'stabile_id');
|
||||
}
|
||||
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(self::class, 'parent_id')->orderBy('sort_order')->orderBy('nome');
|
||||
}
|
||||
|
||||
public function documenti(): HasMany
|
||||
{
|
||||
return $this->hasMany(Documento::class, 'cartella_archivio_id');
|
||||
}
|
||||
|
||||
public function getDisplayPathAttribute(): string
|
||||
{
|
||||
$parts = array_filter([
|
||||
trim((string) ($this->parent?->display_path ?? '')),
|
||||
trim((string) ($this->nome ?? '')),
|
||||
]);
|
||||
|
||||
return $parts === [] ? (string) ($this->nome ?? '') : implode(' / ', $parts);
|
||||
}
|
||||
}
|
||||
141
app/Services/Documenti/DocumentoArchivioVisibilityService.php
Normal file
141
app/Services/Documenti/DocumentoArchivioVisibilityService.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Documenti;
|
||||
|
||||
use App\Models\Documento;
|
||||
use App\Models\DocumentoArchivioCartella;
|
||||
use App\Models\DocumentoStabile;
|
||||
use App\Models\User;
|
||||
use App\Support\StabileContext;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class DocumentoArchivioVisibilityService
|
||||
{
|
||||
public function scopeVisibleDocumenti(Builder $query, User $user): Builder
|
||||
{
|
||||
$activeStabile = StabileContext::getActiveStabile($user);
|
||||
if ($activeStabile !== null) {
|
||||
$query->where('stabile_id', (int) $activeStabile->id);
|
||||
} elseif (! $user->hasAnyRole(['super-admin', 'admin'])) {
|
||||
$allowedIds = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value): int => (int) $value)
|
||||
->all();
|
||||
|
||||
if ($allowedIds === []) {
|
||||
$query->whereRaw('1 = 0');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->whereIn('stabile_id', $allowedIds);
|
||||
}
|
||||
|
||||
return $query->where(function (Builder $documentQuery) use ($user): void {
|
||||
$documentQuery->whereNull('cartella_archivio_id')
|
||||
->where(function (Builder $baseVisibility) use ($user): void {
|
||||
$baseVisibility->whereNull('visibility_scope')
|
||||
->orWhere('visibility_scope', 'interno')
|
||||
->orWhere(function (Builder $restricted) use ($user): void {
|
||||
$this->applyVisibilityConstraint($restricted, $user, 'documenti');
|
||||
});
|
||||
})
|
||||
->orWhereHas('cartellaArchivio', function (Builder $folderQuery) use ($user): void {
|
||||
$folderQuery->where('is_active', true);
|
||||
$this->applyVisibilityConstraint($folderQuery, $user, 'documento_archivio_cartelle');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeVisibleCartelle(Builder $query, User $user, ?int $stabileId = null): Builder
|
||||
{
|
||||
if ($stabileId !== null && $stabileId > 0) {
|
||||
$query->where('stabile_id', $stabileId);
|
||||
} else {
|
||||
$activeStabile = StabileContext::getActiveStabile($user);
|
||||
if ($activeStabile !== null) {
|
||||
$query->where('stabile_id', (int) $activeStabile->id);
|
||||
} elseif (! $user->hasAnyRole(['super-admin', 'admin'])) {
|
||||
$allowedIds = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value): int => (int) $value)
|
||||
->all();
|
||||
|
||||
if ($allowedIds === []) {
|
||||
$query->whereRaw('1 = 0');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->whereIn('stabile_id', $allowedIds);
|
||||
}
|
||||
}
|
||||
|
||||
$query->where('is_active', true);
|
||||
$this->applyVisibilityConstraint($query, $user, 'documento_archivio_cartelle');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function canAccessDocumento(User $user, Documento $documento): bool
|
||||
{
|
||||
return $this->scopeVisibleDocumenti(Documento::query()->whereKey($documento->getKey()), $user)->exists();
|
||||
}
|
||||
|
||||
public function canAccessDocumentoStabile(User $user, DocumentoStabile $documentoStabile): bool
|
||||
{
|
||||
$allowedIds = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value): int => (int) $value)
|
||||
->all();
|
||||
|
||||
return in_array((int) $documentoStabile->stabile_id, $allowedIds, true);
|
||||
}
|
||||
|
||||
private function applyVisibilityConstraint(Builder $query, User $user, string $table): void
|
||||
{
|
||||
$roles = $user->getRoleNames()->map(fn($role): string => (string) $role)->values()->all();
|
||||
$groupLabels = $this->resolveAudienceGroupsForUser($user);
|
||||
$userId = (int) $user->id;
|
||||
|
||||
$query->where(function (Builder $visibilityQuery) use ($table, $roles, $groupLabels, $userId): void {
|
||||
$visibilityQuery->whereNull($table . '.visibility_scope')
|
||||
->orWhere($table . '.visibility_scope', 'interno')
|
||||
->orWhere($table . '.visibility_scope', 'studio')
|
||||
->orWhere(function (Builder $restrictedQuery) use ($table, $roles, $groupLabels, $userId): void {
|
||||
$restrictedQuery->where($table . '.visibility_scope', 'restricted')
|
||||
->where(function (Builder $matchQuery) use ($table, $roles, $groupLabels, $userId): void {
|
||||
if ($roles !== []) {
|
||||
foreach ($roles as $role) {
|
||||
$matchQuery->orWhereJsonContains($table . '.visibility_roles', $role);
|
||||
}
|
||||
}
|
||||
|
||||
if ($groupLabels !== []) {
|
||||
foreach ($groupLabels as $groupLabel) {
|
||||
$matchQuery->orWhereJsonContains($table . '.visibility_groups', $groupLabel);
|
||||
}
|
||||
}
|
||||
|
||||
$matchQuery->orWhereJsonContains($table . '.allowed_user_ids', $userId);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function resolveAudienceGroupsForUser(User $user): array
|
||||
{
|
||||
$groups = [];
|
||||
|
||||
if ($user->hasAnyRole(['super-admin', 'admin', 'amministratore'])) {
|
||||
$groups[] = 'Amministratore';
|
||||
$groups[] = 'Studio completo';
|
||||
}
|
||||
|
||||
if ($user->hasRole('collaboratore')) {
|
||||
$groups[] = 'Collaboratori interni';
|
||||
}
|
||||
|
||||
return array_values(array_unique($groups));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable('documento_archivio_cartelle')) {
|
||||
Schema::create('documento_archivio_cartelle', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('stabile_id')->constrained('stabili')->cascadeOnDelete();
|
||||
$table->foreignId('parent_id')->nullable()->constrained('documento_archivio_cartelle')->nullOnDelete();
|
||||
$table->string('nome');
|
||||
$table->string('slug', 160);
|
||||
$table->string('codice', 80)->nullable();
|
||||
$table->string('percorso_logico')->nullable();
|
||||
$table->string('visibility_scope', 40)->default('interno');
|
||||
$table->json('visibility_roles')->nullable();
|
||||
$table->json('visibility_groups')->nullable();
|
||||
$table->json('allowed_user_ids')->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->unsignedInteger('sort_order')->default(0);
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['stabile_id', 'parent_id', 'slug'], 'doc_arch_folder_parent_slug_unique');
|
||||
$table->index(['stabile_id', 'visibility_scope'], 'doc_arch_folder_stabile_scope_idx');
|
||||
});
|
||||
}
|
||||
|
||||
Schema::table('documenti', function (Blueprint $table): void {
|
||||
if (! Schema::hasColumn('documenti', 'cartella_archivio_id')) {
|
||||
$table->foreignId('cartella_archivio_id')
|
||||
->nullable()
|
||||
->after('stabile_id')
|
||||
->constrained('documento_archivio_cartelle')
|
||||
->nullOnDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('documenti', function (Blueprint $table): void {
|
||||
if (Schema::hasColumn('documenti', 'cartella_archivio_id')) {
|
||||
$table->dropConstrainedForeignId('cartella_archivio_id');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::dropIfExists('documento_archivio_cartelle');
|
||||
}
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
'archivio-digitale' => 'Archivio digitale',
|
||||
'template-drive' => 'Template Drive',
|
||||
'archivio-fisico' => 'Archivio fisico',
|
||||
'permessi' => 'Permessi',
|
||||
'permessi' => 'Permessi e cartelle',
|
||||
];
|
||||
$stats = $this->hubStats;
|
||||
@endphp
|
||||
|
|
@ -108,15 +108,44 @@
|
|||
@else
|
||||
<x-filament::section>
|
||||
<x-slot name="heading">Permessi e visibilita</x-slot>
|
||||
<x-slot name="description">Qui prepariamo la classificazione dei documenti per audience senza mischiare condomini, inquilini, fornitori e uso interno studio.</x-slot>
|
||||
<x-slot name="description">Qui prepariamo la classificazione dei documenti per audience e le cartelle visibili solo a ruoli, gruppi o utenti selezionati.</x-slot>
|
||||
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($this->audienceGroups as $group)
|
||||
<div class="rounded-xl border bg-white px-4 py-3">
|
||||
<div class="text-sm font-semibold text-slate-900">{{ $group }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">Audience candidata per visibilita documentale, export e portali dedicati.</div>
|
||||
<div class="grid gap-4 xl:grid-cols-[minmax(0,1.3fr)_minmax(280px,0.7fr)]">
|
||||
<div>
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($this->visibleFolderRows as $folder)
|
||||
<div class="rounded-xl border bg-white px-4 py-3">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-sm font-semibold text-slate-900">{{ $folder['nome'] }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">{{ $folder['stabile'] }}</div>
|
||||
</div>
|
||||
<div class="rounded-full bg-slate-100 px-2 py-1 text-[11px] font-medium text-slate-700">{{ $folder['scope'] }}</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs text-slate-600">{{ $folder['path'] }}</div>
|
||||
<div class="mt-3 text-xs text-slate-500">Documenti collegati: {{ $folder['documents_count'] }}</div>
|
||||
<div class="mt-2 text-xs text-slate-500">Ruoli: {{ $folder['roles'] !== [] ? implode(', ', $folder['roles']) : 'nessun filtro' }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">Gruppi: {{ $folder['groups'] !== [] ? implode(', ', $folder['groups']) : 'nessun filtro' }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">Utenti: {{ $folder['users'] !== [] ? implode(', ', $folder['users']) : 'nessun utente specifico' }}</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@if(empty($this->visibleFolderRows))
|
||||
<div class="rounded-xl border border-dashed bg-white px-4 py-6 text-sm text-slate-500 md:col-span-2 xl:col-span-3">Nessuna cartella archivio configurata per lo stabile attivo. Usa “Nuova cartella archivio” per iniziare a separare visibilita e struttura.</div>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@foreach($this->audienceGroups as $group)
|
||||
<div class="rounded-xl border bg-white px-4 py-3">
|
||||
<div class="text-sm font-semibold text-slate-900">{{ $group }}</div>
|
||||
<div class="mt-1 text-xs text-slate-500">Audience candidata per visibilita documentale, export e portali dedicati.</div>
|
||||
</div>
|
||||
@endforeach
|
||||
<div class="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-800">
|
||||
Le cartelle ristrette sono applicate sia nella lista documenti sia nei download diretti, cosi l'ID del file non bypassa i permessi dello stabile o della cartella.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
@endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user