Fix FE target stable and simplify acqua ticket flow
This commit is contained in:
parent
16cbec01a0
commit
91bf6713fd
|
|
@ -69,6 +69,18 @@ public function mount(): void
|
||||||
$scope = strtolower(trim((string) request()->query('scope', 'active')));
|
$scope = strtolower(trim((string) request()->query('scope', 'active')));
|
||||||
$this->archivioScope = in_array($scope, ['active', 'all'], true) ? $scope : 'active';
|
$this->archivioScope = in_array($scope, ['active', 'all'], true) ? $scope : 'active';
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
if ($this->servizioFilter && $user instanceof User) {
|
||||||
|
$serviceStabileId = StabileServizio::query()
|
||||||
|
->whereKey((int) $this->servizioFilter)
|
||||||
|
->value('stabile_id');
|
||||||
|
|
||||||
|
$activeStabileId = StabileContext::resolveActiveStabileId($user);
|
||||||
|
if (is_numeric($serviceStabileId) && (int) $serviceStabileId > 0 && (int) $activeStabileId > 0 && (int) $serviceStabileId !== (int) $activeStabileId) {
|
||||||
|
$this->archivioScope = 'all';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->mountInteractsWithTable();
|
$this->mountInteractsWithTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,32 @@ public function getActiveStabileLabelProperty(): string
|
||||||
return $parts !== [] ? implode(' - ', $parts) : ('Stabile #' . (int) $stabile->id);
|
return $parts !== [] ? implode(' - ', $parts) : ('Stabile #' . (int) $stabile->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActiveStabileDestinationSummaryProperty(): string
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (! $user instanceof User) {
|
||||||
|
return 'Stabile non selezionato';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stabile = StabileContext::getActiveStabile($user);
|
||||||
|
if (! $stabile instanceof Stabile) {
|
||||||
|
return 'Stabile non selezionato';
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = $this->getActiveStabileLabelProperty();
|
||||||
|
$cf = $this->resolveStabileSoggettoCf($stabile);
|
||||||
|
|
||||||
|
if ($cf !== '') {
|
||||||
|
$summary .= "\nCodice fiscale soggetto usato per lo scarico: {$cf}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isLikelyTechnicalFeShell($stabile)) {
|
||||||
|
$summary .= "\nATTENZIONE: lo stabile attivo sembra uno shell tecnico FE separato. Per scaricare le fatture del condominio cambia lo stabile in topbar prima di procedere.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $summary;
|
||||||
|
}
|
||||||
|
|
||||||
private function safeUtf8(string $value): string
|
private function safeUtf8(string $value): string
|
||||||
{
|
{
|
||||||
$value = preg_replace('/^\xEF\xBB\xBF/', '', $value) ?? $value;
|
$value = preg_replace('/^\xEF\xBB\xBF/', '', $value) ?? $value;
|
||||||
|
|
@ -230,6 +256,12 @@ public function downloadCassettoQuarter(string $quarter, string $dal, string $al
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isLikelyTechnicalFeShell($stabile)) {
|
||||||
|
$this->notifyTechnicalFeShellBlocked();
|
||||||
|
$this->quarterDownloads[$key] = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$amministratoreId = (int) ($stabile->amministratore_id ?? 0);
|
$amministratoreId = (int) ($stabile->amministratore_id ?? 0);
|
||||||
if ($amministratoreId <= 0) {
|
if ($amministratoreId <= 0) {
|
||||||
Notification::make()->title('Amministratore non valido')->danger()->send();
|
Notification::make()->title('Amministratore non valido')->danger()->send();
|
||||||
|
|
@ -240,7 +272,7 @@ public function downloadCassettoQuarter(string $quarter, string $dal, string $al
|
||||||
if ($this->isCassettoQuarterCompleteFor($amministratoreId, (int) $stabile->id, $dal, $al)) {
|
if ($this->isCassettoQuarterCompleteFor($amministratoreId, (int) $stabile->id, $dal, $al)) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Trimestre già completo')
|
->title('Trimestre già completo')
|
||||||
->body($this->safeUtf8("{$quarter} {$anno} risulta già scaricato (COMPLETO)."))
|
->body($this->safeUtf8("{$quarter} {$anno} risulta già scaricato (COMPLETO). Per rieseguire lo stesso periodo usa 'Scarica da Cassetto Fiscale' e abilita 'Reimport / completa' oppure 'Non saltare'."))
|
||||||
->warning()
|
->warning()
|
||||||
->send();
|
->send();
|
||||||
return;
|
return;
|
||||||
|
|
@ -305,6 +337,44 @@ private function isCassettoQuarterCompleteFor(int $amministratoreId, int $stabil
|
||||||
return $errors === 0 && $filesTotal > 0 && ($imported + $duplicates) >= $filesTotal;
|
return $errors === 0 && $filesTotal > 0 && ($imported + $duplicates) >= $filesTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveStabileSoggettoCf(Stabile $stabile): string
|
||||||
|
{
|
||||||
|
$cf = trim((string) ($stabile->codice_fiscale ?? ''));
|
||||||
|
|
||||||
|
if ($cf === '') {
|
||||||
|
try {
|
||||||
|
$stabile->loadMissing('rubrica');
|
||||||
|
$cf = trim((string) ($stabile->rubrica?->codice_fiscale ?? ''));
|
||||||
|
} catch (\Throwable) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strtoupper(trim($cf));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isLikelyTechnicalFeShell(Stabile $stabile): bool
|
||||||
|
{
|
||||||
|
$stabileCf = $this->resolveStabileSoggettoCf($stabile);
|
||||||
|
$adminCf = strtoupper(trim((string) ($stabile->amministratore?->codice_fiscale_studio ?? '')));
|
||||||
|
$code = strtoupper(trim((string) ($stabile->codice_stabile ?? '')));
|
||||||
|
$legacyCode = trim((string) ($stabile->cod_stabile ?? ''));
|
||||||
|
|
||||||
|
return $stabileCf !== ''
|
||||||
|
&& $adminCf !== ''
|
||||||
|
&& $stabileCf === $adminCf
|
||||||
|
&& (str_starts_with($code, 'FE') || $legacyCode === '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function notifyTechnicalFeShellBlocked(): void
|
||||||
|
{
|
||||||
|
Notification::make()
|
||||||
|
->title('Stabile tecnico FE separato')
|
||||||
|
->body('Lo scarico è bloccato perché lo stabile attivo usa il codice fiscale dello studio/amministratore. Se devi scaricare le fatture del condominio, cambia lo stabile attivo in topbar e seleziona il condominio reale.')
|
||||||
|
->warning()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
public function getArchivioRigheVisteCount(): int
|
public function getArchivioRigheVisteCount(): int
|
||||||
{
|
{
|
||||||
$records = $this->getTableRecords();
|
$records = $this->getTableRecords();
|
||||||
|
|
@ -602,7 +672,7 @@ protected function getHeaderActions(): array
|
||||||
->form([
|
->form([
|
||||||
Placeholder::make('stabile_attivo_info')
|
Placeholder::make('stabile_attivo_info')
|
||||||
->label('Stabile di destinazione')
|
->label('Stabile di destinazione')
|
||||||
->content(fn(): string => $this->getActiveStabileLabelProperty()),
|
->content(fn(): string => $this->getActiveStabileDestinationSummaryProperty()),
|
||||||
|
|
||||||
Select::make('anno')
|
Select::make('anno')
|
||||||
->label('Anno')
|
->label('Anno')
|
||||||
|
|
@ -665,6 +735,9 @@ protected function getHeaderActions(): array
|
||||||
Toggle::make('force_update')
|
Toggle::make('force_update')
|
||||||
->label('Reimport / completa anche se già presente')
|
->label('Reimport / completa anche se già presente')
|
||||||
->default(false),
|
->default(false),
|
||||||
|
Toggle::make('no_skip')
|
||||||
|
->label('Non saltare se lo stesso periodo ha già un log OK')
|
||||||
|
->default(false),
|
||||||
Select::make('importa_righe')
|
Select::make('importa_righe')
|
||||||
->label('Import righe')
|
->label('Import righe')
|
||||||
->options([
|
->options([
|
||||||
|
|
@ -691,6 +764,11 @@ protected function getHeaderActions(): array
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isLikelyTechnicalFeShell($stabile)) {
|
||||||
|
$this->notifyTechnicalFeShellBlocked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$amministratore = $stabile->amministratore;
|
$amministratore = $stabile->amministratore;
|
||||||
if (! $amministratore) {
|
if (! $amministratore) {
|
||||||
Notification::make()->title('Amministratore non trovato')->danger()->send();
|
Notification::make()->title('Amministratore non trovato')->danger()->send();
|
||||||
|
|
@ -707,11 +785,13 @@ protected function getHeaderActions(): array
|
||||||
}
|
}
|
||||||
|
|
||||||
$forceUpdate = (bool) ($data['force_update'] ?? false);
|
$forceUpdate = (bool) ($data['force_update'] ?? false);
|
||||||
|
$noSkip = (bool) ($data['no_skip'] ?? false);
|
||||||
|
$allowRerun = $forceUpdate || $noSkip;
|
||||||
|
|
||||||
// Blocco trimestri: se il trimestre è già completo, evita ri-scarico (salvo force_update).
|
// Blocco trimestri: se il trimestre è già completo, evita ri-scarico (salvo force_update).
|
||||||
$periodo = (string) ($data['periodo'] ?? '');
|
$periodo = (string) ($data['periodo'] ?? '');
|
||||||
$anno = is_numeric($data['anno'] ?? null) ? (int) $data['anno'] : (int) $dal->format('Y');
|
$anno = is_numeric($data['anno'] ?? null) ? (int) $data['anno'] : (int) $dal->format('Y');
|
||||||
if (! $forceUpdate && in_array($periodo, ['Q1', 'Q2', 'Q3', 'Q4'], true)) {
|
if (! $allowRerun && in_array($periodo, ['Q1', 'Q2', 'Q3', 'Q4'], true)) {
|
||||||
[$expectedDal, $expectedAl] = match ($periodo) {
|
[$expectedDal, $expectedAl] = match ($periodo) {
|
||||||
'Q2' => [$anno . '-04-01', $anno . '-06-30'],
|
'Q2' => [$anno . '-04-01', $anno . '-06-30'],
|
||||||
'Q3' => [$anno . '-07-01', $anno . '-09-30'],
|
'Q3' => [$anno . '-07-01', $anno . '-09-30'],
|
||||||
|
|
@ -732,7 +812,7 @@ protected function getHeaderActions(): array
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anti-duplicati veloce: se esiste già un OK per lo stesso periodo, non avviamo niente.
|
// Anti-duplicati veloce: se esiste già un OK per lo stesso periodo, non avviamo niente.
|
||||||
if (! $forceUpdate) {
|
if (! $allowRerun) {
|
||||||
$cf = trim((string) ($stabile->codice_fiscale ?? ''));
|
$cf = trim((string) ($stabile->codice_fiscale ?? ''));
|
||||||
if ($cf === '') {
|
if ($cf === '') {
|
||||||
try {
|
try {
|
||||||
|
|
@ -797,6 +877,7 @@ protected function getHeaderActions(): array
|
||||||
(int) $user->id,
|
(int) $user->id,
|
||||||
[
|
[
|
||||||
'force_update' => (bool) ($data['force_update'] ?? false),
|
'force_update' => (bool) ($data['force_update'] ?? false),
|
||||||
|
'no_skip' => (bool) ($data['no_skip'] ?? false),
|
||||||
'importa_righe' => (string) ($data['importa_righe'] ?? 'auto'),
|
'importa_righe' => (string) ($data['importa_righe'] ?? 'auto'),
|
||||||
'create_fornitore_if_missing' => (bool) ($data['create_fornitore_if_missing'] ?? true),
|
'create_fornitore_if_missing' => (bool) ($data['create_fornitore_if_missing'] ?? true),
|
||||||
],
|
],
|
||||||
|
|
@ -810,7 +891,7 @@ protected function getHeaderActions(): array
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Scarico avviato')
|
->title('Scarico avviato')
|
||||||
->body("Operazione avviata in background. Se già scaricato, verrà saltato automaticamente.\nAggiorna tra poco e controlla la sezione 'Ultimi scarichi'.")
|
->body("Operazione avviata in background. Se hai abilitato 'Reimport / completa' o 'Non saltare', lo stesso periodo verrà rieseguito.\nAggiorna tra poco e controlla la sezione 'Ultimi scarichi'.")
|
||||||
->success()
|
->success()
|
||||||
->send();
|
->send();
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,24 @@ public function updatedUnitaId(): void
|
||||||
$this->syncSuggestedReaderName();
|
$this->syncSuggestedReaderName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatedUnitaSearch(): void
|
||||||
|
{
|
||||||
|
$needle = trim($this->unitaSearch);
|
||||||
|
if ($needle === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filtered = $this->getFilteredUnitaOptionsProperty();
|
||||||
|
if (count($filtered) === 1) {
|
||||||
|
$this->applySelectedUnita((int) ($filtered[0]['id'] ?? 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectUnita(int $unitaId): void
|
||||||
|
{
|
||||||
|
$this->applySelectedUnita($unitaId, true);
|
||||||
|
}
|
||||||
|
|
||||||
public function updatedPendingWaterPhotos(): void
|
public function updatedPendingWaterPhotos(): void
|
||||||
{
|
{
|
||||||
$this->appendPendingWaterPhotos();
|
$this->appendPendingWaterPhotos();
|
||||||
|
|
@ -567,6 +585,14 @@ public function getFilteredUnitaOptionsProperty(): array
|
||||||
return array_values(array_filter($this->unitaOptions, fn(array $option): bool => str_contains((string) ($option['search_text'] ?? ''), $needle)));
|
return array_values(array_filter($this->unitaOptions, fn(array $option): bool => str_contains((string) ($option['search_text'] ?? ''), $needle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int,array{id:int,label:string,reader_label:string,folder_prefix:string,search_text:string}>
|
||||||
|
*/
|
||||||
|
public function getLightUnitaSearchResultsProperty(): array
|
||||||
|
{
|
||||||
|
return array_slice($this->getFilteredUnitaOptionsProperty(), 0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
private function loadPreviousReading(): void
|
private function loadPreviousReading(): void
|
||||||
{
|
{
|
||||||
$this->previousReading = null;
|
$this->previousReading = null;
|
||||||
|
|
@ -875,12 +901,7 @@ private function resolveUnitReaderLabel(UnitaImmobiliare $unita): string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$historic = $unita->nominativiStorici
|
$historicLabel = $this->resolveRelevantHistoricNames($unita)[0] ?? '';
|
||||||
?->sortByDesc(fn($item) => (string) ($item->data_fine ?? '9999-12-31'))
|
|
||||||
->sortByDesc(fn($item) => (string) ($item->data_inizio ?? ''))
|
|
||||||
->first();
|
|
||||||
|
|
||||||
$historicLabel = trim((string) ($historic->nominativo ?? ''));
|
|
||||||
if ($historicLabel !== '') {
|
if ($historicLabel !== '') {
|
||||||
return $historicLabel;
|
return $historicLabel;
|
||||||
}
|
}
|
||||||
|
|
@ -907,12 +928,7 @@ private function resolveUnitSearchNames(UnitaImmobiliare $unita): array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (($unita->nominativiStorici ?? collect()) as $historic) {
|
$names = array_merge($names, $this->resolveRelevantHistoricNames($unita));
|
||||||
$label = trim((string) ($historic->nominativo ?? ''));
|
|
||||||
if ($label !== '') {
|
|
||||||
$names[] = $label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fallback = trim((string) ($unita->denominazione ?? ''));
|
$fallback = trim((string) ($unita->denominazione ?? ''));
|
||||||
if ($fallback !== '') {
|
if ($fallback !== '') {
|
||||||
|
|
@ -922,6 +938,70 @@ private function resolveUnitSearchNames(UnitaImmobiliare $unita): array
|
||||||
return array_values(array_unique(array_filter($names)));
|
return array_values(array_unique(array_filter($names)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int,string>
|
||||||
|
*/
|
||||||
|
private function resolveRelevantHistoricNames(UnitaImmobiliare $unita): array
|
||||||
|
{
|
||||||
|
$today = now()->toDateString();
|
||||||
|
$historic = collect($unita->nominativiStorici ?? []);
|
||||||
|
|
||||||
|
$current = $historic
|
||||||
|
->filter(function ($item) use ($today): bool {
|
||||||
|
$start = trim((string) ($item->data_inizio ?? ''));
|
||||||
|
$end = trim((string) ($item->data_fine ?? ''));
|
||||||
|
|
||||||
|
return ($start === '' || $start <= $today)
|
||||||
|
&& ($end === '' || $end >= $today);
|
||||||
|
})
|
||||||
|
->map(fn($item): string => trim((string) ($item->nominativo ?? '')))
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
if ($current !== []) {
|
||||||
|
return $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $historic
|
||||||
|
->sortByDesc(fn($item): string => sprintf(
|
||||||
|
'%s|%s|%010d',
|
||||||
|
(string) ($item->data_fine ?? '9999-12-31'),
|
||||||
|
(string) ($item->data_inizio ?? ''),
|
||||||
|
(int) ($item->id ?? 0)
|
||||||
|
))
|
||||||
|
->map(fn($item): string => trim((string) ($item->nominativo ?? '')))
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->take(1)
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applySelectedUnita(int $unitaId, bool $syncSearchLabel = false): void
|
||||||
|
{
|
||||||
|
if ($unitaId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->unitaOptions as $option) {
|
||||||
|
if ((int) ($option['id'] ?? 0) !== $unitaId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->unitaId = $unitaId;
|
||||||
|
if ($syncSearchLabel) {
|
||||||
|
$this->unitaSearch = (string) ($option['label'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadPreviousReading();
|
||||||
|
$this->syncSuggestedReaderName();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function buildUnitNaturalSortKey(UnitaImmobiliare $unita, string $readerLabel): string
|
private function buildUnitNaturalSortKey(UnitaImmobiliare $unita, string $readerLabel): string
|
||||||
{
|
{
|
||||||
$parts = array_filter([
|
$parts = array_filter([
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
namespace App\Filament\Pages\Supporto;
|
namespace App\Filament\Pages\Supporto;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class TicketAcquaLight extends TicketAcqua
|
class TicketAcquaLight extends TicketAcqua
|
||||||
|
|
@ -19,4 +21,32 @@ class TicketAcquaLight extends TicketAcqua
|
||||||
protected static ?string $slug = 'supporto/ticket-acqua-light';
|
protected static ?string $slug = 'supporto/ticket-acqua-light';
|
||||||
|
|
||||||
protected string $view = 'filament.pages.supporto.ticket-acqua-light';
|
protected string $view = 'filament.pages.supporto.ticket-acqua-light';
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
parent::mount();
|
||||||
|
|
||||||
|
$this->resetLightUnitSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedStabileId(): void
|
||||||
|
{
|
||||||
|
parent::updatedStabileId();
|
||||||
|
|
||||||
|
$this->resetLightUnitSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resetLightUnitSelection(): void
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$this->unitaId = null;
|
||||||
|
$this->unitaSearch = '';
|
||||||
|
$this->previousReading = null;
|
||||||
|
$this->suggestedReaderName = null;
|
||||||
|
|
||||||
|
if ($user instanceof User) {
|
||||||
|
$this->rilevatoreNome = trim((string) $user->name) ?: $this->rilevatoreNome;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,25 @@ public function ingest(
|
||||||
return ['status' => 'error', 'message' => 'Stabile non disponibile'];
|
return ['status' => 'error', 'message' => 'Stabile non disponibile'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$storedPayload = [];
|
||||||
|
$storedRaw = is_string($fattura->consumo_raw ?? null) ? trim((string) $fattura->consumo_raw) : '';
|
||||||
|
if ($storedRaw !== '') {
|
||||||
|
$decoded = json_decode($storedRaw, true);
|
||||||
|
if (is_array($decoded) && (($decoded['type'] ?? null) === 'acqua')) {
|
||||||
|
$storedPayload = $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((empty($parsed['codici']) || ! is_array($parsed['codici'])) && is_array($storedPayload['codici'] ?? null)) {
|
||||||
|
$parsed['codici'] = $storedPayload['codici'];
|
||||||
|
}
|
||||||
|
if ((empty($parsed['contatore']) || ! is_array($parsed['contatore'])) && is_array($storedPayload['contatore'] ?? null)) {
|
||||||
|
$parsed['contatore'] = $storedPayload['contatore'];
|
||||||
|
}
|
||||||
|
if ((empty($parsed['consumi']) || ! is_array($parsed['consumi'])) && is_array($storedPayload['consumi'] ?? null)) {
|
||||||
|
$parsed['consumi'] = $storedPayload['consumi'];
|
||||||
|
}
|
||||||
|
|
||||||
$codici = is_array($parsed['codici'] ?? null) ? $parsed['codici'] : [];
|
$codici = is_array($parsed['codici'] ?? null) ? $parsed['codici'] : [];
|
||||||
$contatore = is_array($parsed['contatore'] ?? null) ? $parsed['contatore'] : [];
|
$contatore = is_array($parsed['contatore'] ?? null) ? $parsed['contatore'] : [];
|
||||||
$consumi = is_array($parsed['consumi'] ?? null) ? $parsed['consumi'] : [];
|
$consumi = is_array($parsed['consumi'] ?? null) ? $parsed['consumi'] : [];
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1 @@
|
||||||
@php
|
|
||||||
$user = \Illuminate\Support\Facades\Auth::user();
|
|
||||||
$version = config('netgescon.version') ?? config('app.version');
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
@once
|
|
||||||
<style>
|
|
||||||
/* Ensure the Filament layout behaves like a full-height flex column,
|
|
||||||
so this footer is pushed to the bottom even on short pages.
|
|
||||||
This is a safe fallback when the Vite theme CSS isn't loaded. */
|
|
||||||
.fi-main-ctn {
|
|
||||||
min-height: 100vh;
|
|
||||||
min-height: 100dvh;
|
|
||||||
height: auto !important;
|
|
||||||
display: flex !important;
|
|
||||||
flex-direction: column !important;
|
|
||||||
}
|
|
||||||
.fi-main {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
min-height: 0;
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
.netgescon-footer {
|
|
||||||
flex: none;
|
|
||||||
margin-top: auto;
|
|
||||||
width: 100% !important;
|
|
||||||
position: sticky !important;
|
|
||||||
bottom: 0 !important;
|
|
||||||
z-index: 10 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@endonce
|
|
||||||
|
|
||||||
<div class="netgescon-footer px-6 py-3 text-xs text-gray-500 border-t bg-white">
|
|
||||||
<div class="mx-auto max-w-7xl flex flex-wrap items-center justify-between gap-2">
|
|
||||||
<div class="flex flex-wrap items-center gap-x-3 gap-y-1">
|
|
||||||
<div>NetGescon{{ $version ? ' · v' . $version : '' }}</div>
|
|
||||||
<div class="text-gray-400">|</div>
|
|
||||||
<div>{{ now()->format('d/m/Y H:i') }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<a href="{{ \App\Filament\Pages\Supporto\RichiestaHelp::getUrl(panel: 'admin-filament') }}" class="text-primary-600 hover:underline">
|
|
||||||
Richiedi help / segnala bug
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -46,19 +46,44 @@
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="block text-sm">
|
<label class="block text-sm">
|
||||||
<span class="mb-1 block font-medium">Cerca nominativo / unità</span>
|
<span class="mb-1 block font-medium">Cerca unità / nominativo</span>
|
||||||
<input type="text" wire:model.live.debounce.250ms="unitaSearch" class="w-full rounded-lg border-gray-300" placeholder="Es. Maggiore, Int. 10, B_4..." />
|
<input type="text" wire:model.live.debounce.250ms="unitaSearch" class="w-full rounded-lg border-gray-300" placeholder="Es. Rossi, Int. 10, 0021-A-20..." />
|
||||||
|
<div class="mt-1 text-[11px] text-slate-500">Un solo campo: cerca per codice unità, interno, piano o nominativo attuale dello stabile selezionato.</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="block text-sm">
|
@php
|
||||||
<span class="mb-1 block font-medium">Unità immobiliare</span>
|
$searchResults = $this->lightUnitaSearchResults;
|
||||||
<select wire:model.live="unitaId" class="w-full rounded-lg border-gray-300">
|
$selectedUnit = collect($this->unitaOptions)->firstWhere('id', (int) ($unitaId ?? 0));
|
||||||
<option value="">Seleziona unità</option>
|
$searchTerm = trim((string) $unitaSearch);
|
||||||
@foreach($this->filteredUnitaOptions as $option)
|
@endphp
|
||||||
<option value="{{ $option['id'] }}">{{ $option['label'] }}</option>
|
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-3">
|
||||||
@endforeach
|
@if($selectedUnit)
|
||||||
</select>
|
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm text-emerald-900">
|
||||||
</label>
|
<div class="font-semibold">Unità selezionata</div>
|
||||||
|
<div class="mt-1">{{ $selectedUnit['label'] }}</div>
|
||||||
|
@if(filled($selectedUnit['reader_label'] ?? null))
|
||||||
|
<div class="mt-1 text-[11px] text-emerald-800">Nominativo collegato: {{ $selectedUnit['reader_label'] }}</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@elseif($searchTerm === '')
|
||||||
|
<div class="text-sm text-slate-500">Digita almeno parte del nominativo o del codice unità per selezionare rapidamente il destinatario corretto.</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($searchTerm !== '' && $searchResults !== [])
|
||||||
|
<div class="mt-3 space-y-2">
|
||||||
|
@foreach($searchResults as $option)
|
||||||
|
<button type="button" wire:click="selectUnita({{ (int) $option['id'] }})" class="block w-full rounded-xl border border-slate-200 bg-white px-3 py-2 text-left text-sm shadow-sm transition hover:border-sky-300 hover:bg-sky-50">
|
||||||
|
<div class="font-medium text-slate-900">{{ $option['label'] }}</div>
|
||||||
|
@if(filled($option['reader_label'] ?? null))
|
||||||
|
<div class="mt-1 text-[11px] text-slate-500">{{ $option['reader_label'] }}</div>
|
||||||
|
@endif
|
||||||
|
</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@elseif($searchTerm !== '' && $searchResults === [])
|
||||||
|
<div class="mt-3 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900">Nessuna unità trovata per questa ricerca nello stabile selezionato.</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="block text-sm">
|
<label class="block text-sm">
|
||||||
<span class="mb-1 block font-medium">Nominativo letturista</span>
|
<span class="mb-1 block font-medium">Nominativo letturista</span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user