Fix water Blade parse errors and common meter moves

This commit is contained in:
michele 2026-04-13 15:54:47 +00:00
parent 7dd54f3a6f
commit 16cbec01a0
3 changed files with 179 additions and 29 deletions

View File

@ -631,6 +631,88 @@ public function table(Table $table): Table
->send();
}),
Action::make('spostaContatoreComune')
->label('Sposta a contatore comune')
->icon('heroicon-o-arrow-path-rounded-square')
->color('warning')
->form([
Select::make('stabile_servizio_id')
->label('Contatore / servizio comune di destinazione')
->options(fn(StabileServizioLettura $record) => $this->getCommonWaterServiceOptions((int) $record->stabile_id))
->searchable()
->required(),
Select::make('rilevatore_tipo')
->label('Tipo rilevatore')
->options($this->getReaderTypeOptions()),
TextInput::make('rilevatore_nome')->label('Nominativo rilevatore')->maxLength(191),
Textarea::make('motivo')->label('Motivo spostamento')->rows(3)->maxLength(1000),
])
->fillForm(fn(StabileServizioLettura $record): array => [
'stabile_servizio_id' => $record->stabile_servizio_id,
'rilevatore_tipo' => (string) ($record->rilevatore_tipo ?? ''),
'rilevatore_nome' => (string) ($record->rilevatore_nome ?? ''),
'motivo' => '',
])
->visible(fn(StabileServizioLettura $record): bool => strtolower(trim((string) ($record->servizio?->tipo ?? ''))) === 'acqua')
->action(function (StabileServizioLettura $record, array $data): void {
$targetServiceId = (int) ($data['stabile_servizio_id'] ?? 0);
$targetService = StabileServizio::query()
->where('id', $targetServiceId)
->where('stabile_id', (int) $record->stabile_id)
->where('tipo', 'acqua')
->first();
if (! $targetService instanceof StabileServizio) {
Notification::make()->title('Servizio comune non valido')->danger()->send();
return;
}
$raw = is_array($record->raw ?? null) ? $record->raw : [];
$movements = is_array($raw['common_meter_movements'] ?? null) ? $raw['common_meter_movements'] : [];
$movements[] = [
'from_stabile_servizio_id' => (int) ($record->getOriginal('stabile_servizio_id') ?? 0) ?: null,
'to_stabile_servizio_id' => (int) $targetService->id,
'from_unita_immobiliare_id'=> (int) ($record->getOriginal('unita_immobiliare_id') ?? 0) ?: null,
'to_unita_immobiliare_id' => null,
'note' => trim((string) ($data['motivo'] ?? '')),
'changed_by' => Auth::id(),
'changed_at' => now()->toIso8601String(),
];
$raw['common_meter_movements'] = $movements;
$raw['reader_assignment'] = [
'user_id' => null,
'name' => trim((string) ($data['rilevatore_nome'] ?? '')) ?: (trim((string) ($record->rilevatore_nome ?? '')) ?: null),
'type' => trim((string) ($data['rilevatore_tipo'] ?? '')) ?: (trim((string) ($record->rilevatore_tipo ?? '')) ?: null),
];
$raw['moved_to_common_meter'] = [
'service_id' => (int) $targetService->id,
'service_name' => (string) ($targetService->nome ?? ''),
'common_asset' => (string) data_get($targetService->meta, 'common_asset_label', ''),
'counter_scope' => (string) data_get($targetService->meta, 'counter_scope', ''),
];
$record->fill([
'stabile_servizio_id' => (int) $targetService->id,
'unita_immobiliare_id' => null,
'fornitore_id' => $targetService->fornitore_id,
'rilevatore_tipo' => trim((string) ($data['rilevatore_tipo'] ?? '')) ?: $record->rilevatore_tipo,
'rilevatore_nome' => trim((string) ($data['rilevatore_nome'] ?? '')) ?: $record->rilevatore_nome,
'tipologia_lettura' => 'autolettura_contatore_generale',
'lettura_precedente_valore' => null,
'lettura_precedente_foto_path' => null,
'raw' => $raw,
]);
$record->save();
$this->hydrateReadingWithPreviousData($record);
Notification::make()
->title('Lettura spostata su contatore comune')
->body('La lettura è ora agganciata al servizio corretto del contatore comune.')
->success()
->send();
}),
Action::make('edit')
->label('Modifica')
->icon('heroicon-o-pencil-square')
@ -977,6 +1059,38 @@ private function getServiziOptionsByTipo(string $tipo): array
->all();
}
/**
* @return array<int, string>
*/
private function getCommonWaterServiceOptions(int $stabileId): array
{
return StabileServizio::query()
->where('stabile_id', $stabileId)
->where('tipo', 'acqua')
->where('attivo', true)
->orderBy('nome')
->orderBy('contatore_matricola')
->get(['id', 'nome', 'contatore_matricola', 'meta'])
->mapWithKeys(function (StabileServizio $service): array {
$name = trim((string) ($service->nome ?? '')) ?: ('Servizio #' . (int) $service->id);
$asset = trim((string) data_get($service->meta, 'common_asset_label', ''));
$servedArea = trim((string) data_get($service->meta, 'served_area_label', ''));
$counterScope = trim((string) data_get($service->meta, 'counter_scope', ''));
$matricola = trim((string) ($service->contatore_matricola ?? ''));
$parts = array_filter([
$name,
$asset,
$servedArea,
$counterScope !== '' ? 'Contatore ' . $counterScope : null,
$matricola !== '' ? 'Matricola ' . $matricola : null,
]);
return [(int) $service->id => implode(' - ', $parts)];
})
->all();
}
private function scheduleWaterCampaign(array $data): int
{
$user = Auth::user();

View File

@ -316,15 +316,27 @@
$gpsLng = data_get($metadata, 'gps_decimal.lng');
$hasGps = is_numeric($gpsLat) && is_numeric($gpsLng);
$device = trim((string) data_get($metadata, 'device', ''));
$detailRows = [];
if ($hasGps) {
$detailRows[] = [
'label' => 'Coordinate',
'value' => number_format((float) $gpsLat, 5, '.', '') . ', ' . number_format((float) $gpsLng, 5, '.', ''),
];
}
if ($device !== '') {
$detailRows[] = [
'label' => 'Dispositivo',
'value' => $device,
];
}
@endphp
@if($hasGps || $device !== '')
@if($detailRows !== [])
<div class="mt-2 rounded-lg border border-slate-200 bg-white p-2 text-[11px] text-slate-700">
@if($hasGps)
<div><span class="font-medium">Coordinate:</span> {{ number_format((float) $gpsLat, 5, '.', '') }}, {{ number_format((float) $gpsLng, 5, '.', '') }}</div>
@endif
@if($device !== '')
<div><span class="font-medium">Dispositivo:</span> {{ $device }}</div>
@endif
@foreach($detailRows as $detail)
<div><span class="font-medium">{{ $detail['label'] }}:</span> {{ $detail['value'] }}</div>
@endforeach
</div>
@endif
<label class="mt-2 block">

View File

@ -286,29 +286,53 @@
$hasGps = is_numeric($gpsLat) && is_numeric($gpsLng);
$exifDate = trim((string) data_get($metadata, 'exif_datetime', ''));
$device = trim((string) data_get($metadata, 'device', ''));
$captureSource = trim((string) ($metadata['capture_source'] ?? ''));
$badgeRows = [];
$detailRows = [];
if ($captureSource === 'camera') {
$badgeRows[] = ['class' => 'bg-amber-100 text-amber-800', 'label' => 'GPS da browser'];
} elseif ($captureSource === 'attachment') {
$badgeRows[] = ['class' => 'bg-indigo-100 text-indigo-800', 'label' => 'EXIF da libreria'];
}
if ($hasGps) {
$detailRows[] = [
'label' => 'Coordinate',
'value' => number_format((float) $gpsLat, 5, '.', '') . ', ' . number_format((float) $gpsLng, 5, '.', ''),
];
}
if ($exifDate !== '') {
$detailRows[] = ['label' => 'Data EXIF', 'value' => $exifDate];
}
if ($device !== '') {
$detailRows[] = ['label' => 'Dispositivo', 'value' => $device];
}
if ($captureSource !== '') {
$detailRows[] = [
'label' => 'Sorgente metadati',
'value' => $captureSource === 'camera' ? 'browser/camera live' : $captureSource,
];
}
@endphp
@if($hasGps || $exifDate !== '' || $device !== '')
<div class="mt-2 flex flex-wrap gap-1">
@if(($metadata['capture_source'] ?? null) === 'camera')
<span class="inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold text-amber-800">GPS da browser</span>
@elseif(($metadata['capture_source'] ?? null) === 'attachment')
<span class="inline-flex items-center rounded-full bg-indigo-100 px-2 py-0.5 text-[10px] font-semibold text-indigo-800">EXIF da libreria</span>
@endif
</div>
<div class="mt-2 rounded-lg border border-slate-200 bg-white p-2 text-[11px] text-slate-700">
@if($hasGps)
<div><span class="font-medium">Coordinate:</span> {{ number_format((float) $gpsLat, 5, '.', '') }}, {{ number_format((float) $gpsLng, 5, '.', '') }}</div>
@endif
@if($exifDate !== '')
<div><span class="font-medium">Data EXIF:</span> {{ $exifDate }}</div>
@endif
@if($device !== '')
<div><span class="font-medium">Dispositivo:</span> {{ $device }}</div>
@endif
@if(!empty($metadata['capture_source']))
<div><span class="font-medium">Sorgente metadati:</span> {{ $metadata['capture_source'] === 'camera' ? 'browser/camera live' : $metadata['capture_source'] }}</div>
@endif
</div>
@if($badgeRows !== [] || $detailRows !== [])
@if($badgeRows !== [])
<div class="mt-2 flex flex-wrap gap-1">
@foreach($badgeRows as $badge)
<span class="inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold {{ $badge['class'] }}">{{ $badge['label'] }}</span>
@endforeach
</div>
@endif
@if($detailRows !== [])
<div class="mt-2 rounded-lg border border-slate-200 bg-white p-2 text-[11px] text-slate-700">
@foreach($detailRows as $detail)
<div><span class="font-medium">{{ $detail['label'] }}:</span> {{ $detail['value'] }}</div>
@endforeach
</div>
@endif
@endif
<label class="mt-2 block">
<span class="mb-1 block text-[11px] font-medium">Nota sotto la foto</span>