Fix supplier view and add public water reading flow
This commit is contained in:
parent
016177bce0
commit
887540c5fe
|
|
@ -299,7 +299,20 @@ public function mount(): void
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
if ($user instanceof User) {
|
if ($user instanceof User) {
|
||||||
|
$requestedStabileId = request()->integer('stabile_id');
|
||||||
|
if ($requestedStabileId > 0) {
|
||||||
|
StabileContext::setActiveStabileId($user, $requestedStabileId);
|
||||||
|
}
|
||||||
|
|
||||||
$this->stabileId = StabileContext::resolveActiveStabileId($user);
|
$this->stabileId = StabileContext::resolveActiveStabileId($user);
|
||||||
|
|
||||||
|
if (! $this->stabileId) {
|
||||||
|
$fallbackStabileId = StabileContext::accessibleStabili($user)->first()?->id;
|
||||||
|
if ($fallbackStabileId) {
|
||||||
|
StabileContext::setActiveStabileId($user, (int) $fallbackStabileId);
|
||||||
|
$this->stabileId = (int) $fallbackStabileId;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->mountInteractsWithTable();
|
$this->mountInteractsWithTable();
|
||||||
|
|
@ -355,7 +368,7 @@ protected function getTableQuery(): Builder
|
||||||
return LegacyCondominNominativo::query()->whereRaw('1 = 0');
|
return LegacyCondominNominativo::query()->whereRaw('1 = 0');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->hasLegacyNominativiForStabile($codStabile) || $this->hasLegacyDuplicateUnits($codStabile)) {
|
if (! $this->hasLegacyNominativiForStabile($codStabile)) {
|
||||||
return $this->buildDomainFallbackQuery((int) $activeStabileId);
|
return $this->buildDomainFallbackQuery((int) $activeStabileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,20 +411,6 @@ protected function hasLegacyNominativiForStabile(string $codStabile): bool
|
||||||
->exists();
|
->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function hasLegacyDuplicateUnits(string $codStabile): bool
|
|
||||||
{
|
|
||||||
return DB::connection('gescon_import')
|
|
||||||
->table('condomin')
|
|
||||||
->where('cod_stabile', $codStabile)
|
|
||||||
->whereNotNull('interno')
|
|
||||||
->where('interno', '<>', '')
|
|
||||||
->select('scala', 'interno')
|
|
||||||
->groupBy('scala', 'interno')
|
|
||||||
->havingRaw('COUNT(*) > 1')
|
|
||||||
->limit(1)
|
|
||||||
->exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildDomainFallbackQuery(int $stabileId): Builder
|
protected function buildDomainFallbackQuery(int $stabileId): Builder
|
||||||
{
|
{
|
||||||
$today = now()->toDateString();
|
$today = now()->toDateString();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
use App\Models\StabileServizio;
|
use App\Models\StabileServizio;
|
||||||
use App\Models\StabileServizioLettura;
|
use App\Models\StabileServizioLettura;
|
||||||
use App\Models\StabileServizioTariffa;
|
use App\Models\StabileServizioTariffa;
|
||||||
|
use App\Models\UnitaImmobiliare;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\VoceSpesa;
|
use App\Models\VoceSpesa;
|
||||||
use App\Support\AnnoGestioneContext;
|
use App\Support\AnnoGestioneContext;
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use UnitEnum;
|
use UnitEnum;
|
||||||
|
|
||||||
class ServiziStabileArchivio extends Page implements HasTable
|
class ServiziStabileArchivio extends Page implements HasTable
|
||||||
|
|
@ -69,6 +71,121 @@ public function setAcquaTab(string $tab): void
|
||||||
$this->acquaTab = in_array($tab, $allowed, true) ? $tab : 'dashboard';
|
$this->acquaTab = in_array($tab, $allowed, true) ? $tab : 'dashboard';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function prepareAcquaReadingCampaign(): void
|
||||||
|
{
|
||||||
|
$stabileId = $this->resolveActiveStabileId();
|
||||||
|
$servizio = $this->resolveActiveAcquaServizio();
|
||||||
|
|
||||||
|
if (! $stabileId || ! $servizio) {
|
||||||
|
Notification::make()->title('Servizio acqua attivo non disponibile per lo stabile selezionato.')->warning()->send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$units = UnitaImmobiliare::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->orderBy('scala')
|
||||||
|
->orderBy('interno')
|
||||||
|
->orderBy('id')
|
||||||
|
->get(['id', 'codice_unita', 'scala', 'interno']);
|
||||||
|
|
||||||
|
if ($units->isEmpty()) {
|
||||||
|
Notification::make()->title('Nessuna unità immobiliare disponibile per avviare la campagna letture.')->warning()->send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$year = $this->resolveActiveAnnoGestione();
|
||||||
|
$created = 0;
|
||||||
|
$updated = 0;
|
||||||
|
|
||||||
|
foreach ($units as $unit) {
|
||||||
|
$completed = StabileServizioLettura::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->where('stabile_servizio_id', (int) $servizio->id)
|
||||||
|
->where('unita_immobiliare_id', (int) $unit->id)
|
||||||
|
->whereYear('created_at', $year)
|
||||||
|
->whereNotNull('lettura_fine')
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($completed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$previous = StabileServizioLettura::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->where('stabile_servizio_id', (int) $servizio->id)
|
||||||
|
->where('unita_immobiliare_id', (int) $unit->id)
|
||||||
|
->whereNotNull('lettura_fine')
|
||||||
|
->orderByDesc('periodo_al')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$requestRow = StabileServizioLettura::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->where('stabile_servizio_id', (int) $servizio->id)
|
||||||
|
->where('unita_immobiliare_id', (int) $unit->id)
|
||||||
|
->whereYear('created_at', $year)
|
||||||
|
->whereNull('lettura_fine')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$requestReference = 'PUBREQ:' . (int) $servizio->id . ':' . (int) $unit->id . ':' . $year;
|
||||||
|
$requestPayload = [
|
||||||
|
'stabile_id' => $stabileId,
|
||||||
|
'stabile_servizio_id' => (int) $servizio->id,
|
||||||
|
'unita_immobiliare_id' => (int) $unit->id,
|
||||||
|
'fornitore_id' => $servizio->fornitore_id,
|
||||||
|
'periodo_dal' => $previous?->periodo_al,
|
||||||
|
'periodo_al' => null,
|
||||||
|
'tipologia_lettura' => 'richiesta_autolettura',
|
||||||
|
'canale_acquisizione' => 'portale_pubblico',
|
||||||
|
'riferimento_acquisizione' => $requestReference,
|
||||||
|
'workflow_stato' => 'richiesta_inviata',
|
||||||
|
'richiesta_lettura_inviata_at' => now(),
|
||||||
|
'deadline_lettura_at' => now()->addDays(7),
|
||||||
|
'lettura_precedente_valore' => $previous?->lettura_fine,
|
||||||
|
'lettura_inizio' => $previous?->lettura_fine,
|
||||||
|
'raw' => [
|
||||||
|
'source' => 'campagna_letture_acqua',
|
||||||
|
'year' => $year,
|
||||||
|
'unit_label' => trim((string) ($unit->codice_unita ?? '') . ' ' . (string) ($unit->interno ?? '')),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($requestRow) {
|
||||||
|
$requestRow->fill($requestPayload);
|
||||||
|
$requestRow->save();
|
||||||
|
$updated++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StabileServizioLettura::query()->create($requestPayload);
|
||||||
|
$created++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Campagna letture acqua preparata')
|
||||||
|
->body("Richieste create: {$created} · richieste aggiornate: {$updated}")
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markAcquaReadingReminder(int $readingId): void
|
||||||
|
{
|
||||||
|
$reading = StabileServizioLettura::query()->find($readingId);
|
||||||
|
if (! $reading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$reading->fill([
|
||||||
|
'sollecito_inviato_at' => now(),
|
||||||
|
'workflow_stato' => 'sollecito_inviato',
|
||||||
|
]);
|
||||||
|
$reading->save();
|
||||||
|
|
||||||
|
Notification::make()->title('Sollecito registrato')->success()->send();
|
||||||
|
}
|
||||||
|
|
||||||
public function ensureAcquaAceaContract(): void
|
public function ensureAcquaAceaContract(): void
|
||||||
{
|
{
|
||||||
$stabileId = $this->resolveActiveStabileId();
|
$stabileId = $this->resolveActiveStabileId();
|
||||||
|
|
@ -478,6 +595,34 @@ private function resolveActiveAnnoGestione(): int
|
||||||
return AnnoGestioneContext::resolveActiveAnno($user instanceof User ? $user : null);
|
return AnnoGestioneContext::resolveActiveAnno($user instanceof User ? $user : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveActiveAcquaServizio(): ?StabileServizio
|
||||||
|
{
|
||||||
|
$stabileId = $this->resolveActiveStabileId();
|
||||||
|
if (! $stabileId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StabileServizio::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->where('tipo', 'acqua')
|
||||||
|
->orderByDesc('attivo')
|
||||||
|
->orderBy('id')
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPublicAcquaReadingUrl(int $servizioId, int $unitaId, ?int $requestId = null): string
|
||||||
|
{
|
||||||
|
return URL::temporarySignedRoute(
|
||||||
|
'public.water-reading.show',
|
||||||
|
now()->addDays(30),
|
||||||
|
array_filter([
|
||||||
|
'servizio' => $servizioId,
|
||||||
|
'unita' => $unitaId,
|
||||||
|
'request' => $requestId,
|
||||||
|
], fn ($value) => $value !== null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** @return array<int, string> */
|
/** @return array<int, string> */
|
||||||
private function resolveLegacyCodiciStabile(): array
|
private function resolveLegacyCodiciStabile(): array
|
||||||
{
|
{
|
||||||
|
|
@ -1201,6 +1346,107 @@ public function getAcquaLettureCondominiProperty(): array
|
||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return array{totale:int,completate:int,pendenti:int,sollecitate:int} */
|
||||||
|
public function getAcquaCampagnaStatsProperty(): array
|
||||||
|
{
|
||||||
|
$rows = $this->acquaCampagnaRows;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'totale' => count($rows),
|
||||||
|
'completate' => count(array_filter($rows, fn (array $row): bool => $row['status'] === 'fatta')),
|
||||||
|
'pendenti' => count(array_filter($rows, fn (array $row): bool => in_array($row['status'], ['richiesta', 'non_richiesta'], true))),
|
||||||
|
'sollecitate' => count(array_filter($rows, fn (array $row): bool => $row['status'] === 'sollecito')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return array<int,array<string,mixed>> */
|
||||||
|
public function getAcquaCampagnaRowsProperty(): array
|
||||||
|
{
|
||||||
|
$stabileId = $this->resolveActiveStabileId();
|
||||||
|
$servizio = $this->resolveActiveAcquaServizio();
|
||||||
|
if (! $stabileId || ! $servizio) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$year = $this->resolveActiveAnnoGestione();
|
||||||
|
|
||||||
|
$units = UnitaImmobiliare::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->orderBy('scala')
|
||||||
|
->orderBy('interno')
|
||||||
|
->orderBy('id')
|
||||||
|
->get(['id', 'codice_unita', 'denominazione', 'scala', 'interno']);
|
||||||
|
|
||||||
|
$nominativi = DB::table('unita_immobiliare_nominativi')
|
||||||
|
->whereIn('unita_immobiliare_id', $units->pluck('id')->all())
|
||||||
|
->orderByDesc('updated_at')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->get(['unita_immobiliare_id', 'nominativo']);
|
||||||
|
|
||||||
|
$nominativiByUnit = [];
|
||||||
|
foreach ($nominativi as $row) {
|
||||||
|
$unitId = (int) ($row->unita_immobiliare_id ?? 0);
|
||||||
|
if ($unitId > 0 && ! isset($nominativiByUnit[$unitId])) {
|
||||||
|
$nominativiByUnit[$unitId] = trim((string) ($row->nominativo ?? ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$readingRows = StabileServizioLettura::query()
|
||||||
|
->where('stabile_id', $stabileId)
|
||||||
|
->where('stabile_servizio_id', (int) $servizio->id)
|
||||||
|
->whereIn('unita_immobiliare_id', $units->pluck('id')->all())
|
||||||
|
->orderByDesc('created_at')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$currentYearByUnit = [];
|
||||||
|
$latestRealByUnit = [];
|
||||||
|
foreach ($readingRows as $row) {
|
||||||
|
$unitId = (int) ($row->unita_immobiliare_id ?? 0);
|
||||||
|
if ($unitId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rowYear = optional($row->created_at)->year ?: optional($row->periodo_al)->year;
|
||||||
|
if ((int) $rowYear === $year && ! isset($currentYearByUnit[$unitId])) {
|
||||||
|
$currentYearByUnit[$unitId] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($row->lettura_fine !== null && ! isset($latestRealByUnit[$unitId])) {
|
||||||
|
$latestRealByUnit[$unitId] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $units->map(function (UnitaImmobiliare $unit) use ($currentYearByUnit, $latestRealByUnit, $nominativiByUnit, $servizio): array {
|
||||||
|
$current = $currentYearByUnit[(int) $unit->id] ?? null;
|
||||||
|
$latestReal = $latestRealByUnit[(int) $unit->id] ?? null;
|
||||||
|
|
||||||
|
$status = 'non_richiesta';
|
||||||
|
if ($current && $current->lettura_fine !== null) {
|
||||||
|
$status = 'fatta';
|
||||||
|
} elseif ($current && $current->sollecito_inviato_at) {
|
||||||
|
$status = 'sollecito';
|
||||||
|
} elseif ($current && $current->richiesta_lettura_inviata_at) {
|
||||||
|
$status = 'richiesta';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'unit_id' => (int) $unit->id,
|
||||||
|
'unit_label' => trim((string) ($unit->codice_unita ?? '') . ' ' . (string) ($unit->interno ?? '')) ?: ('UI #' . (int) $unit->id),
|
||||||
|
'nominativo' => $nominativiByUnit[(int) $unit->id] ?? (trim((string) ($unit->denominazione ?? '')) ?: 'Nominativo da completare'),
|
||||||
|
'previous_reading' => $latestReal?->lettura_fine,
|
||||||
|
'request_id' => $current?->id,
|
||||||
|
'status' => $status,
|
||||||
|
'requested_at' => optional($current?->richiesta_lettura_inviata_at)->format('d/m/Y H:i'),
|
||||||
|
'deadline_at' => optional($current?->deadline_lettura_at)->format('d/m/Y'),
|
||||||
|
'completed_at' => optional($current?->created_at)->format('d/m/Y H:i'),
|
||||||
|
'latest_value' => $current?->lettura_fine,
|
||||||
|
'public_url' => $this->buildPublicAcquaReadingUrl((int) $servizio->id, (int) $unit->id, $current?->id ? (int) $current->id : null),
|
||||||
|
];
|
||||||
|
})->all();
|
||||||
|
}
|
||||||
|
|
||||||
/** @return array<int, array<string,mixed>> */
|
/** @return array<int, array<string,mixed>> */
|
||||||
public function getAcquaTariffeRowsProperty(): array
|
public function getAcquaTariffeRowsProperty(): array
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\PublicAccess;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\StabileServizio;
|
||||||
|
use App\Models\StabileServizioLettura;
|
||||||
|
use App\Models\UnitaImmobiliare;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
|
|
||||||
|
class WaterReadingRequestController extends Controller
|
||||||
|
{
|
||||||
|
public function show(Request $request): View
|
||||||
|
{
|
||||||
|
$servizioId = (int) $request->integer('servizio');
|
||||||
|
$unitaId = (int) $request->integer('unita');
|
||||||
|
$requestId = (int) $request->integer('request');
|
||||||
|
|
||||||
|
$servizio = StabileServizio::query()
|
||||||
|
->where('id', $servizioId)
|
||||||
|
->where('tipo', 'acqua')
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$unita = UnitaImmobiliare::query()
|
||||||
|
->with('stabile:id,denominazione,codice_stabile')
|
||||||
|
->where('id', $unitaId)
|
||||||
|
->where('stabile_id', (int) $servizio->stabile_id)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$pendingRequest = $requestId > 0
|
||||||
|
? StabileServizioLettura::query()
|
||||||
|
->where('id', $requestId)
|
||||||
|
->where('stabile_servizio_id', $servizioId)
|
||||||
|
->where('unita_immobiliare_id', $unitaId)
|
||||||
|
->first()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
$previous = StabileServizioLettura::query()
|
||||||
|
->where('stabile_servizio_id', $servizioId)
|
||||||
|
->where('unita_immobiliare_id', $unitaId)
|
||||||
|
->whereNotNull('lettura_fine')
|
||||||
|
->when($pendingRequest, fn ($query) => $query->where('id', '!=', (int) $pendingRequest->id))
|
||||||
|
->orderByDesc('periodo_al')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$submitUrl = URL::temporarySignedRoute('public.water-reading.store', now()->addDays(30), array_filter([
|
||||||
|
'servizio' => $servizioId,
|
||||||
|
'unita' => $unitaId,
|
||||||
|
'request' => $pendingRequest?->id,
|
||||||
|
]));
|
||||||
|
|
||||||
|
return view('public.water-reading.show', [
|
||||||
|
'servizio' => $servizio,
|
||||||
|
'unita' => $unita,
|
||||||
|
'pendingRequest' => $pendingRequest,
|
||||||
|
'previous' => $previous,
|
||||||
|
'submitUrl' => $submitUrl,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$servizioId = (int) $request->integer('servizio');
|
||||||
|
$unitaId = (int) $request->integer('unita');
|
||||||
|
$requestId = (int) $request->integer('request');
|
||||||
|
|
||||||
|
$servizio = StabileServizio::query()
|
||||||
|
->where('id', $servizioId)
|
||||||
|
->where('tipo', 'acqua')
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$unita = UnitaImmobiliare::query()
|
||||||
|
->where('id', $unitaId)
|
||||||
|
->where('stabile_id', (int) $servizio->stabile_id)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$validated = $request->validate([
|
||||||
|
'lettura_attuale' => ['required', 'numeric', 'min:0'],
|
||||||
|
'data_lettura' => ['nullable', 'date'],
|
||||||
|
'rilevatore_nome' => ['required', 'string', 'max:255'],
|
||||||
|
'rilevatore_contatto' => ['nullable', 'string', 'max:255'],
|
||||||
|
'note' => ['nullable', 'string', 'max:2000'],
|
||||||
|
'foto_1' => ['nullable', 'image', 'max:8192'],
|
||||||
|
'foto_2' => ['nullable', 'image', 'max:8192'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pendingRequest = $requestId > 0
|
||||||
|
? StabileServizioLettura::query()
|
||||||
|
->where('id', $requestId)
|
||||||
|
->where('stabile_servizio_id', $servizioId)
|
||||||
|
->where('unita_immobiliare_id', $unitaId)
|
||||||
|
->first()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
$previous = StabileServizioLettura::query()
|
||||||
|
->where('stabile_servizio_id', $servizioId)
|
||||||
|
->where('unita_immobiliare_id', $unitaId)
|
||||||
|
->whereNotNull('lettura_fine')
|
||||||
|
->when($pendingRequest, fn ($query) => $query->where('id', '!=', (int) $pendingRequest->id))
|
||||||
|
->orderByDesc('periodo_al')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$letturaInizio = $previous?->lettura_fine !== null ? (float) $previous->lettura_fine : null;
|
||||||
|
$letturaFine = (float) $validated['lettura_attuale'];
|
||||||
|
$consumo = $letturaInizio !== null ? round($letturaFine - $letturaInizio, 3) : null;
|
||||||
|
|
||||||
|
if ($consumo !== null && $consumo < 0) {
|
||||||
|
return back()->withErrors([
|
||||||
|
'lettura_attuale' => 'La lettura attuale non puo essere inferiore a quella precedente.',
|
||||||
|
])->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
$photoPaths = [];
|
||||||
|
foreach (['foto_1', 'foto_2'] as $photoField) {
|
||||||
|
if ($request->hasFile($photoField)) {
|
||||||
|
$photoPaths[] = $request->file($photoField)?->store('letture-acqua-public', 'public');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'stabile_id' => (int) $servizio->stabile_id,
|
||||||
|
'stabile_servizio_id' => (int) $servizio->id,
|
||||||
|
'unita_immobiliare_id' => (int) $unita->id,
|
||||||
|
'fornitore_id' => $servizio->fornitore_id,
|
||||||
|
'periodo_dal' => $previous?->periodo_al,
|
||||||
|
'periodo_al' => isset($validated['data_lettura']) ? Carbon::parse((string) $validated['data_lettura'])->toDateString() : now()->toDateString(),
|
||||||
|
'tipologia_lettura' => 'autolettura_link_pubblico',
|
||||||
|
'canale_acquisizione' => 'portale_pubblico',
|
||||||
|
'workflow_stato' => 'ricevuta',
|
||||||
|
'riferimento_acquisizione' => $pendingRequest?->riferimento_acquisizione ?: ('PUBREAD:' . (int) $servizio->id . ':' . (int) $unita->id . ':' . now()->format('YmdHis')),
|
||||||
|
'rilevatore_tipo' => 'link_pubblico',
|
||||||
|
'rilevatore_nome' => trim((string) $validated['rilevatore_nome']),
|
||||||
|
'lettura_precedente_valore' => $letturaInizio,
|
||||||
|
'lettura_inizio' => $letturaInizio,
|
||||||
|
'lettura_fine' => $letturaFine,
|
||||||
|
'lettura_foto_path' => $photoPaths[0] ?? null,
|
||||||
|
'lettura_foto_metadata' => [
|
||||||
|
'paths' => $photoPaths,
|
||||||
|
'contact' => trim((string) ($validated['rilevatore_contatto'] ?? '')),
|
||||||
|
'note' => trim((string) ($validated['note'] ?? '')),
|
||||||
|
],
|
||||||
|
'consumo_valore' => $consumo,
|
||||||
|
'consumo_unita' => 'mc',
|
||||||
|
'raw' => [
|
||||||
|
'source' => 'public_water_reading_link',
|
||||||
|
'contact' => trim((string) ($validated['rilevatore_contatto'] ?? '')),
|
||||||
|
'note' => trim((string) ($validated['note'] ?? '')),
|
||||||
|
'photo_paths' => $photoPaths,
|
||||||
|
'request_id' => $pendingRequest?->id,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($pendingRequest) {
|
||||||
|
$pendingRequest->fill($payload);
|
||||||
|
$pendingRequest->save();
|
||||||
|
} else {
|
||||||
|
StabileServizioLettura::query()->create($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', 'Lettura acqua inviata correttamente.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
$legacyOpsRows = $this->acquaLegacyOperazioniRows;
|
$legacyOpsRows = $this->acquaLegacyOperazioniRows;
|
||||||
$acquaContratto = $this->acquaContrattoStabile;
|
$acquaContratto = $this->acquaContrattoStabile;
|
||||||
$acquaRipartoNominativi = $this->acquaRipartoNominativiLegacy;
|
$acquaRipartoNominativi = $this->acquaRipartoNominativiLegacy;
|
||||||
|
$campagnaStats = $this->acquaCampagnaStats;
|
||||||
|
$campagnaRows = $this->acquaCampagnaRows;
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
|
|
@ -236,6 +238,90 @@ class="pb-3 px-1 border-b-2 font-medium text-sm {{ $acquaTab === 'servizi' ? 'bo
|
||||||
|
|
||||||
@if($acquaTab === 'letture')
|
@if($acquaTab === 'letture')
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
|
<div class="rounded-lg border border-emerald-200 bg-emerald-50 p-4 dark:border-emerald-700/50 dark:bg-emerald-900/10">
|
||||||
|
<div class="mb-3 flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<div class="text-sm font-semibold text-emerald-900 dark:text-emerald-200">Campagna letture acqua per unità</div>
|
||||||
|
<div class="mt-1 text-xs text-emerald-800 dark:text-emerald-300">Primo slice operativo: genera richieste per le unità dello stabile, dividi fatte e non fatte, e usa un link pubblico firmato per inviare lettura attuale più massimo due foto.</div>
|
||||||
|
</div>
|
||||||
|
<x-filament::button size="sm" color="success" wire:click="prepareAcquaReadingCampaign">
|
||||||
|
Prepara / aggiorna campagna letture
|
||||||
|
</x-filament::button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-3 md:grid-cols-4">
|
||||||
|
<div class="rounded-lg border bg-white p-3 text-sm dark:bg-gray-900">
|
||||||
|
<div class="text-xs uppercase tracking-wide text-gray-500">Unità</div>
|
||||||
|
<div class="mt-2 text-xl font-semibold">{{ $campagnaStats['totale'] }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg border bg-white p-3 text-sm dark:bg-gray-900">
|
||||||
|
<div class="text-xs uppercase tracking-wide text-gray-500">Fatte</div>
|
||||||
|
<div class="mt-2 text-xl font-semibold text-emerald-700">{{ $campagnaStats['completate'] }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg border bg-white p-3 text-sm dark:bg-gray-900">
|
||||||
|
<div class="text-xs uppercase tracking-wide text-gray-500">Pendenti</div>
|
||||||
|
<div class="mt-2 text-xl font-semibold text-amber-700">{{ $campagnaStats['pendenti'] }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg border bg-white p-3 text-sm dark:bg-gray-900">
|
||||||
|
<div class="text-xs uppercase tracking-wide text-gray-500">Sollecitate</div>
|
||||||
|
<div class="mt-2 text-xl font-semibold text-sky-700">{{ $campagnaStats['sollecitate'] }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4 overflow-x-auto">
|
||||||
|
<table class="min-w-full text-xs">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-emerald-200 dark:border-emerald-700/50">
|
||||||
|
<th class="px-2 py-2 text-left">Unità</th>
|
||||||
|
<th class="px-2 py-2 text-left">Nominativo</th>
|
||||||
|
<th class="px-2 py-2 text-right">Lettura precedente</th>
|
||||||
|
<th class="px-2 py-2 text-left">Stato</th>
|
||||||
|
<th class="px-2 py-2 text-left">Richiesta</th>
|
||||||
|
<th class="px-2 py-2 text-left">Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($campagnaRows as $row)
|
||||||
|
<tr class="border-b border-emerald-100 dark:border-emerald-800/30">
|
||||||
|
<td class="px-2 py-2">{{ $row['unit_label'] }}</td>
|
||||||
|
<td class="px-2 py-2">{{ $row['nominativo'] }}</td>
|
||||||
|
<td class="px-2 py-2 text-right">{{ $row['previous_reading'] !== null ? number_format((float) $row['previous_reading'], 3, ',', '.') : '—' }}</td>
|
||||||
|
<td class="px-2 py-2">
|
||||||
|
@if($row['status'] === 'fatta')
|
||||||
|
<span class="rounded bg-emerald-100 px-1.5 py-0.5 font-semibold text-emerald-700">Fatta</span>
|
||||||
|
@elseif($row['status'] === 'sollecito')
|
||||||
|
<span class="rounded bg-sky-100 px-1.5 py-0.5 font-semibold text-sky-700">Sollecito</span>
|
||||||
|
@elseif($row['status'] === 'richiesta')
|
||||||
|
<span class="rounded bg-amber-100 px-1.5 py-0.5 font-semibold text-amber-700">Richiesta inviata</span>
|
||||||
|
@else
|
||||||
|
<span class="rounded bg-slate-100 px-1.5 py-0.5 font-semibold text-slate-700">Da inviare</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2">
|
||||||
|
{{ $row['requested_at'] ?: '—' }}
|
||||||
|
@if($row['deadline_at'])
|
||||||
|
<div class="text-[10px] text-gray-500">Scadenza {{ $row['deadline_at'] }}</div>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="px-2 py-2">
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<a href="{{ $row['public_url'] }}" target="_blank" class="inline-flex items-center rounded-md bg-white px-2 py-1 text-[11px] font-medium text-emerald-800 ring-1 ring-inset ring-emerald-300 hover:bg-emerald-100">Apri link</a>
|
||||||
|
@if($row['request_id'] && $row['status'] !== 'fatta')
|
||||||
|
<button type="button" wire:click="markAcquaReadingReminder({{ (int) $row['request_id'] }})" class="inline-flex items-center rounded-md bg-slate-100 px-2 py-1 text-[11px] font-medium text-slate-700 hover:bg-slate-200">Sollecito</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="px-2 py-4 text-center text-emerald-700 dark:text-emerald-300">Nessuna unità disponibile per la campagna letture.</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-700/50 dark:bg-slate-900/20">
|
<div class="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-700/50 dark:bg-slate-900/20">
|
||||||
<div class="mb-2 text-sm font-semibold text-slate-900 dark:text-slate-100">Base nominativi riparto ACQUA (legacy) per inserimento letture manuali</div>
|
<div class="mb-2 text-sm font-semibold text-slate-900 dark:text-slate-100">Base nominativi riparto ACQUA (legacy) per inserimento letture manuali</div>
|
||||||
<div class="mb-2 text-xs text-slate-600 dark:text-slate-300">
|
<div class="mb-2 text-xs text-slate-600 dark:text-slate-300">
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
<a href="{{ \App\Filament\Pages\Fornitore\LavorazioniOperative::getUrl(['fornitore' => (int) $this->fornitore->id], panel: 'admin-filament') }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Lavorazioni</a>
|
<a href="{{ \App\Filament\Pages\Fornitore\LavorazioniOperative::getUrl(['fornitore' => (int) $this->fornitore->id], panel: 'admin-filament') }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Lavorazioni</a>
|
||||||
<a href="{{ $this->getCollaboratoriUrl() }}" class="inline-flex items-center rounded-md bg-primary-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-500">Collaboratori</a>
|
<a href="{{ $this->getCollaboratoriUrl() }}" class="inline-flex items-center rounded-md bg-primary-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-500">Collaboratori</a>
|
||||||
<a href="{{ $this->getRubricaUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Rubrica clienti</a>
|
<a href="{{ $this->getRubricaUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Rubrica clienti</a>
|
||||||
|
<a href="{{ \App\Filament\Pages\Fornitore\ImpostazioniArchivio::getUrl(['fornitore' => (int) $this->fornitore->id], panel: 'admin-filament') }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Impostazioni</a>
|
||||||
<a href="{{ $this->getProdottiUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Prodotti</a>
|
<a href="{{ $this->getProdottiUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Prodotti</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -596,8 +597,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
@if($attachmentPreview)
|
@if($attachmentPreview)
|
||||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" x-data="{ zoom: 1, pdfPage: 1, iframeKey: 0, refreshPdf() { this.iframeKey++; }, zoomIn() { this.zoom = Math.min(this.zoom + 0.2, 4); }, zoomOut() { this.zoom = Math.max(this.zoom - 0.2, 0.5); }, resetZoom() { this.zoom = 1; }, nextPage() { this.pdfPage = this.pdfPage + 1; this.refreshPdf(); }, prevPage() { this.pdfPage = Math.max(this.pdfPage - 1, 1); this.refreshPdf(); }, printPdf() { const frame = this.$refs.pdfFrame; if (frame?.contentWindow) { frame.contentWindow.focus(); frame.contentWindow.print(); } else { window.open(@js($attachmentPreview['url']), '_blank'); } } }">
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" x-data="{ zoom: 1, pdfPage: 1, iframeKey: 0, refreshPdf() { this.iframeKey++; }, zoomIn() { this.zoom = Math.min(this.zoom + 0.2, 4); }, zoomOut() { this.zoom = Math.max(this.zoom - 0.2, 0.5); }, resetZoom() { this.zoom = 1; }, nextPage() { this.pdfPage = this.pdfPage + 1; this.refreshPdf(); }, prevPage() { this.pdfPage = Math.max(this.pdfPage - 1, 1); this.refreshPdf(); }, printPdf() { const frame = this.$refs.pdfFrame; if (frame?.contentWindow) { frame.contentWindow.focus(); frame.contentWindow.print(); } else { window.open(@js($attachmentPreview['url']), '_blank'); } } }">
|
||||||
<div class="h-[92vh] w-full max-w-[96vw] rounded-xl bg-white shadow-2xl">
|
<div class="h-[92vh] w-full max-w-[96vw] rounded-xl bg-white shadow-2xl">
|
||||||
|
|
@ -643,4 +642,5 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
</x-filament-panels::page>
|
</x-filament-panels::page>
|
||||||
|
|
@ -123,7 +123,7 @@ class="inline-flex h-4 w-4 items-center justify-center rounded-full text-slate-5
|
||||||
<div class="mt-1 text-xs text-indigo-700">Tag: {{ $match->tags }}</div>
|
<div class="mt-1 text-xs text-indigo-700">Tag: {{ $match->tags }}</div>
|
||||||
@endif
|
@endif
|
||||||
@if($match->note)
|
@if($match->note)
|
||||||
<div class="mt-1 text-xs text-gray-500">{{ \\Illuminate\\Support\\Str::limit((string) $match->note, 120) }}</div>
|
<div class="mt-1 text-xs text-gray-500">{{ \Illuminate\Support\Str::limit((string) $match->note, 120) }}</div>
|
||||||
@endif
|
@endif
|
||||||
</button>
|
</button>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
|
||||||
84
resources/views/public/water-reading/show.blade.php
Normal file
84
resources/views/public/water-reading/show.blade.php
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
|
<title>Lettura acqua - {{ $unita->stabile->denominazione ?? 'NetGescon' }}</title>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body class="min-h-screen bg-slate-100 text-slate-900">
|
||||||
|
<div class="mx-auto max-w-3xl space-y-4 p-4 md:p-8">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-semibold">Invio lettura acqua</h1>
|
||||||
|
<p class="mt-1 text-sm text-slate-600">Compila la lettura attuale del contatore per l'unità indicata. Puoi allegare massimo due foto.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (session('success'))
|
||||||
|
<div class="rounded-lg border border-emerald-300 bg-emerald-50 p-3 text-sm text-emerald-700">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="rounded-lg border border-red-300 bg-red-50 p-3 text-sm text-red-700">
|
||||||
|
<ul class="list-disc pl-5">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="rounded-lg bg-white p-4 shadow">
|
||||||
|
<div class="grid gap-3 md:grid-cols-2 text-sm">
|
||||||
|
<div><span class="font-medium">Stabile:</span> {{ $unita->stabile->codice_stabile ?? '' }} - {{ $unita->stabile->denominazione ?? '-' }}</div>
|
||||||
|
<div><span class="font-medium">Unità:</span> {{ $unita->codice_unita ?: ('UI #' . (int) $unita->id) }}</div>
|
||||||
|
<div><span class="font-medium">Servizio:</span> {{ $servizio->nome ?: 'Acqua' }}</div>
|
||||||
|
<div><span class="font-medium">Matricola:</span> {{ $servizio->contatore_matricola ?: '—' }}</div>
|
||||||
|
<div><span class="font-medium">Lettura precedente:</span> {{ $previous?->lettura_fine !== null ? number_format((float) $previous->lettura_fine, 3, ',', '.') : '—' }}</div>
|
||||||
|
<div><span class="font-medium">Ultima data nota:</span> {{ optional($previous?->periodo_al)->format('d/m/Y') ?: '—' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" action="{{ $submitUrl }}" enctype="multipart/form-data" class="space-y-4 rounded-lg bg-white p-4 shadow">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Chi sta inviando la lettura</span>
|
||||||
|
<input type="text" name="rilevatore_nome" value="{{ old('rilevatore_nome') }}" class="w-full rounded-md border-slate-300" required>
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Contatto</span>
|
||||||
|
<input type="text" name="rilevatore_contatto" value="{{ old('rilevatore_contatto') }}" class="w-full rounded-md border-slate-300" placeholder="Telefono o email">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Lettura attuale</span>
|
||||||
|
<input type="number" step="0.001" min="0" name="lettura_attuale" value="{{ old('lettura_attuale') }}" class="w-full rounded-md border-slate-300" required>
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Data lettura</span>
|
||||||
|
<input type="date" name="data_lettura" value="{{ old('data_lettura', now()->toDateString()) }}" class="w-full rounded-md border-slate-300">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid gap-4 md:grid-cols-2">
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Foto contatore 1</span>
|
||||||
|
<input type="file" name="foto_1" accept="image/*" capture="environment" class="w-full rounded-md border-slate-300">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Foto contatore 2</span>
|
||||||
|
<input type="file" name="foto_2" accept="image/*" capture="environment" class="w-full rounded-md border-slate-300">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="block text-sm">
|
||||||
|
<span class="mb-1 block font-medium">Note</span>
|
||||||
|
<textarea name="note" rows="3" class="w-full rounded-md border-slate-300">{{ old('note') }}</textarea>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit" class="inline-flex items-center rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white">Invia lettura</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -45,6 +45,11 @@
|
||||||
return redirect('/admin-filament');
|
return redirect('/admin-filament');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::middleware('signed')->prefix('public/letture-acqua')->name('public.water-reading.')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\PublicAccess\WaterReadingRequestController::class, 'show'])->name('show');
|
||||||
|
Route::post('/', [\App\Http\Controllers\PublicAccess\WaterReadingRequestController::class, 'store'])->name('store');
|
||||||
|
});
|
||||||
|
|
||||||
// --- Authenticated Routes ---
|
// --- Authenticated Routes ---
|
||||||
Route::middleware(['auth', 'verified'])->group(function () {
|
Route::middleware(['auth', 'verified'])->group(function () {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user