667 lines
27 KiB
PHP
Executable File
667 lines
27 KiB
PHP
Executable File
<?php
|
||
namespace App\Filament\Pages\Condomini;
|
||
|
||
use App\Filament\Pages\Strumenti\DocumentiArchivio;
|
||
use App\Models\Documento;
|
||
use App\Models\FatturaElettronica;
|
||
use App\Models\Fornitore;
|
||
use App\Models\Scadenza;
|
||
use App\Models\Stabile;
|
||
use App\Models\StabileContratto;
|
||
use App\Models\StabileServizio;
|
||
use App\Models\User;
|
||
use App\Services\Consumi\AcquaContractSyncService;
|
||
use App\Support\StabileContext;
|
||
use BackedEnum;
|
||
use Filament\Actions\Action;
|
||
use Filament\Forms\Components\DatePicker;
|
||
use Filament\Forms\Components\Repeater;
|
||
use Filament\Forms\Components\Select;
|
||
use Filament\Forms\Components\Textarea;
|
||
use Filament\Forms\Components\TextInput;
|
||
use Filament\Forms\Components\Toggle;
|
||
use Filament\Notifications\Notification;
|
||
use Filament\Pages\Page;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\Auth;
|
||
use Illuminate\Support\Facades\Schema;
|
||
use Illuminate\Support\Str;
|
||
use UnitEnum;
|
||
|
||
class ContrattiHub extends Page
|
||
{
|
||
protected static ?string $navigationLabel = 'HUB Contratti';
|
||
|
||
protected static ?string $title = 'HUB Contratti';
|
||
|
||
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-clipboard-document-list';
|
||
|
||
protected static UnitEnum|string|null $navigationGroup = 'Stabile';
|
||
|
||
protected static ?int $navigationSort = 34;
|
||
|
||
protected static ?string $slug = 'condomini/contratti-hub';
|
||
|
||
protected string $view = 'filament.pages.condomini.contratti-hub';
|
||
|
||
public ?Stabile $stabileAttivo = null;
|
||
|
||
public static function canAccess(): bool
|
||
{
|
||
$user = Auth::user();
|
||
|
||
return $user instanceof User
|
||
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
|
||
}
|
||
|
||
public function mount(): void
|
||
{
|
||
$user = Auth::user();
|
||
if (! $user instanceof User) {
|
||
return;
|
||
}
|
||
|
||
$activeId = StabileContext::resolveActiveStabileId($user);
|
||
if (! $activeId) {
|
||
return;
|
||
}
|
||
|
||
$this->stabileAttivo = StabileContext::accessibleStabili($user)->firstWhere('id', $activeId);
|
||
}
|
||
|
||
protected function getHeaderActions(): array
|
||
{
|
||
return [
|
||
Action::make('registra_contratto')
|
||
->label('Registra contratto')
|
||
->icon('heroicon-o-plus')
|
||
->form($this->getContrattoFormSchema())
|
||
->action(function (array $data): void {
|
||
$this->createContratto($data);
|
||
}),
|
||
Action::make('allinea_contratto_acqua')
|
||
->label('Allinea contratto acqua')
|
||
->icon('heroicon-o-arrow-path')
|
||
->action(function (): void {
|
||
$this->syncAcquaContract();
|
||
}),
|
||
Action::make('apri_documenti')
|
||
->label('Archivio documenti')
|
||
->icon('heroicon-o-folder-open')
|
||
->url(fn(): string => DocumentiArchivio::getUrl(panel: 'admin-filament')),
|
||
];
|
||
}
|
||
|
||
public function getStatsProperty(): array
|
||
{
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
if ($stabileId <= 0) {
|
||
return ['totale' => 0, 'attivi' => 0, 'scadenze' => 0, 'documenti' => 0];
|
||
}
|
||
|
||
$query = StabileContratto::query()->where('stabile_id', $stabileId);
|
||
|
||
return [
|
||
'totale' => (clone $query)->count(),
|
||
'attivi' => (clone $query)->where('stato', 'attivo')->count(),
|
||
'scadenze' => Scadenza::query()
|
||
->where('stabile_id', $stabileId)
|
||
->whereNotNull('stabile_contratto_id')
|
||
->whereDate('data_scadenza', '>=', now()->toDateString())
|
||
->whereDate('data_scadenza', '<=', now()->addDays(45)->toDateString())
|
||
->count(),
|
||
'documenti' => Documento::query()
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo_documento', 'contratto')
|
||
->count(),
|
||
];
|
||
}
|
||
|
||
public function getContrattiRowsProperty(): Collection
|
||
{
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
if ($stabileId <= 0) {
|
||
return collect();
|
||
}
|
||
|
||
return StabileContratto::query()
|
||
->with(['fornitore:id,ragione_sociale,nome,cognome', 'documentoPrincipale:id,nome,numero_protocollo'])
|
||
->where('stabile_id', $stabileId)
|
||
->orderByRaw("CASE WHEN stato = 'attivo' THEN 0 WHEN stato = 'in_revisione' THEN 1 ELSE 2 END")
|
||
->orderByDesc('decorrenza_dal')
|
||
->orderByDesc('id')
|
||
->get();
|
||
}
|
||
|
||
public function getUpcomingScadenzeProperty(): Collection
|
||
{
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
if ($stabileId <= 0) {
|
||
return collect();
|
||
}
|
||
|
||
return Scadenza::query()
|
||
->with('stabileContratto:id,titolo,tipo_contratto')
|
||
->where('stabile_id', $stabileId)
|
||
->whereNotNull('stabile_contratto_id')
|
||
->whereDate('data_scadenza', '>=', now()->subDays(7)->toDateString())
|
||
->orderBy('data_scadenza')
|
||
->limit(10)
|
||
->get();
|
||
}
|
||
|
||
public function getAcquaSnapshotProperty(): array
|
||
{
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
if ($stabileId <= 0) {
|
||
return [
|
||
'servizio_nome' => null,
|
||
'fornitore' => null,
|
||
'utenza' => null,
|
||
'cliente' => null,
|
||
'contratto' => null,
|
||
'matricola' => null,
|
||
'contratto_hub_id' => null,
|
||
];
|
||
}
|
||
|
||
$servizio = StabileServizio::query()
|
||
->with('fornitore:id,ragione_sociale,nome,cognome')
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo', 'acqua')
|
||
->orderByDesc('attivo')
|
||
->orderBy('id')
|
||
->first();
|
||
|
||
$contratto = StabileContratto::query()
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo_contratto', 'acqua')
|
||
->orderByRaw("CASE WHEN stato = 'attivo' THEN 0 ELSE 1 END")
|
||
->orderByDesc('id')
|
||
->first();
|
||
|
||
$fornitoreLabel = trim((string) ($servizio?->fornitore?->ragione_sociale ?: trim(($servizio?->fornitore?->nome ?? '') . ' ' . ($servizio?->fornitore?->cognome ?? ''))));
|
||
|
||
return [
|
||
'servizio_nome' => $servizio?->nome,
|
||
'fornitore' => $fornitoreLabel !== '' ? $fornitoreLabel : null,
|
||
'utenza' => $servizio?->codice_utenza,
|
||
'cliente' => $servizio?->codice_cliente,
|
||
'contratto' => $servizio?->codice_contratto,
|
||
'matricola' => $servizio?->contatore_matricola,
|
||
'contratto_hub_id' => $contratto?->id,
|
||
];
|
||
}
|
||
|
||
/** @return array<string,mixed> */
|
||
public function getAcquaExtractionDiagnosticsProperty(): array
|
||
{
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
if ($stabileId <= 0) {
|
||
return ['latest' => null, 'counts' => ['totale' => 0, 'con_identificativi' => 0, 'senza_identificativi' => 0, 'con_pagamento' => 0], 'warnings' => []];
|
||
}
|
||
|
||
$servizio = StabileServizio::query()
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo', 'acqua')
|
||
->orderByDesc('attivo')
|
||
->orderBy('id')
|
||
->first(['id', 'codice_utenza', 'codice_cliente', 'codice_contratto', 'contatore_matricola']);
|
||
|
||
$rows = FatturaElettronica::query()
|
||
->where('stabile_id', $stabileId)
|
||
->whereNotNull('consumo_raw')
|
||
->orderByDesc('data_fattura')
|
||
->orderByDesc('id')
|
||
->limit(30)
|
||
->get(['id', 'numero_fattura', 'data_fattura', 'consumo_raw'])
|
||
->map(function (FatturaElettronica $fattura): ?array {
|
||
$payload = json_decode((string) $fattura->consumo_raw, true);
|
||
if (! $this->payloadLooksLikeAcqua(is_array($payload) ? $payload : null)) {
|
||
return null;
|
||
}
|
||
|
||
return [
|
||
'id' => (int) $fattura->id,
|
||
'numero_fattura' => trim((string) ($fattura->numero_fattura ?? '')),
|
||
'data_fattura' => optional($fattura->data_fattura)->format('d/m/Y'),
|
||
'utenza' => trim((string) data_get($payload, 'codici.utenza', '')) ?: null,
|
||
'cliente' => trim((string) data_get($payload, 'codici.cliente', '')) ?: null,
|
||
'contratto' => trim((string) data_get($payload, 'codici.contratto', '')) ?: null,
|
||
'matricola' => trim((string) data_get($payload, 'contatore.matricola', '')) ?: null,
|
||
'cbill' => trim((string) data_get($payload, 'pagamento.cbill', '')) ?: null,
|
||
'codice_avviso' => trim((string) data_get($payload, 'pagamento.codice_avviso', '')) ?: null,
|
||
'sia' => trim((string) data_get($payload, 'pagamento.sia', '')) ?: null,
|
||
];
|
||
})
|
||
->filter()
|
||
->values();
|
||
|
||
$latest = $rows->first();
|
||
$warnings = [];
|
||
|
||
if ($latest && $servizio instanceof StabileServizio) {
|
||
foreach ([
|
||
'utenza' => (string) ($servizio->codice_utenza ?? ''),
|
||
'cliente' => (string) ($servizio->codice_cliente ?? ''),
|
||
'contratto' => (string) ($servizio->codice_contratto ?? ''),
|
||
'matricola' => (string) ($servizio->contatore_matricola ?? ''),
|
||
] as $key => $currentValue) {
|
||
$currentValue = trim($currentValue);
|
||
$extractedValue = trim((string) ($latest[$key] ?? ''));
|
||
|
||
if ($currentValue === '' && $extractedValue !== '') {
|
||
$warnings[] = 'Manca ' . $key . ' sul servizio acqua ma è presente nell’ultima FE: ' . $extractedValue;
|
||
} elseif ($currentValue !== '' && $extractedValue !== '' && $this->normalizeIdentifier($currentValue) !== $this->normalizeIdentifier($extractedValue)) {
|
||
$warnings[] = 'Disallineamento ' . $key . ': servizio=' . $currentValue . ' / FE=' . $extractedValue;
|
||
}
|
||
}
|
||
}
|
||
|
||
return [
|
||
'latest' => $latest,
|
||
'counts' => [
|
||
'totale' => $rows->count(),
|
||
'con_identificativi' => $rows->filter(fn(array $row): bool => ! empty($row['utenza']) || ! empty($row['cliente']) || ! empty($row['contratto']) || ! empty($row['matricola']))->count(),
|
||
'senza_identificativi' => $rows->filter(fn(array $row): bool => empty($row['utenza']) && empty($row['cliente']) && empty($row['contratto']) && empty($row['matricola']))->count(),
|
||
'con_pagamento' => $rows->filter(fn(array $row): bool => ! empty($row['cbill']) || ! empty($row['codice_avviso']) || ! empty($row['sia']))->count(),
|
||
],
|
||
'warnings' => $warnings,
|
||
];
|
||
}
|
||
|
||
private function payloadLooksLikeAcqua(?array $payload): bool
|
||
{
|
||
if (! is_array($payload) || $payload === []) {
|
||
return false;
|
||
}
|
||
|
||
if (($payload['type'] ?? null) === 'acqua') {
|
||
return true;
|
||
}
|
||
|
||
return is_array($payload['consumi'] ?? null)
|
||
|| is_array($payload['riepilogo_letture'] ?? null)
|
||
|| is_array($payload['finestra_autolettura'] ?? null)
|
||
|| is_array($payload['codici'] ?? null);
|
||
}
|
||
|
||
private function normalizeIdentifier(string $value): string
|
||
{
|
||
return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim($value))) ?? '';
|
||
}
|
||
|
||
protected function getContrattoFormSchema(): array
|
||
{
|
||
return [
|
||
Select::make('stabile_id')
|
||
->label('Stabile')
|
||
->options(fn(): array=> $this->getStabiliOptions())
|
||
->default(fn(): ?int => $this->stabileAttivo?->id)
|
||
->searchable()
|
||
->required(),
|
||
Select::make('fornitore_id')
|
||
->label('Fornitore / manutentore')
|
||
->options(fn(): array=> $this->getFornitoriOptions())
|
||
->searchable()
|
||
->nullable(),
|
||
Select::make('tipo_contratto')
|
||
->label('Tipo contratto')
|
||
->options($this->getTipoContrattoOptions())
|
||
->required(),
|
||
TextInput::make('titolo')
|
||
->label('Titolo')
|
||
->required()
|
||
->maxLength(255),
|
||
TextInput::make('servizio_label')
|
||
->label('Servizio / utenza')
|
||
->placeholder('Es. Ascensore scala A, Acqua potabile, Luce comune')
|
||
->maxLength(255),
|
||
TextInput::make('codice_contratto')
|
||
->label('Codice contratto')
|
||
->maxLength(80),
|
||
TextInput::make('riferimento_esterno')
|
||
->label('Riferimento esterno')
|
||
->placeholder('Es. numero cliente, riferimento pratica, ID POD/PDR principale')
|
||
->maxLength(120),
|
||
DatePicker::make('data_stipula')
|
||
->label('Data stipula'),
|
||
DatePicker::make('decorrenza_dal')
|
||
->label('Decorrenza dal')
|
||
->required(),
|
||
DatePicker::make('decorrenza_al')
|
||
->label('Decorrenza al'),
|
||
Select::make('frequenza_scadenze')
|
||
->label('Frequenza scadenze')
|
||
->options($this->getFrequenzaOptions())
|
||
->default('annuale')
|
||
->required(),
|
||
TextInput::make('giorno_scadenza')
|
||
->label('Giorno scadenza')
|
||
->numeric()
|
||
->minValue(1)
|
||
->maxValue(28),
|
||
TextInput::make('giorni_preavviso')
|
||
->label('Giorni preavviso')
|
||
->numeric()
|
||
->default(30)
|
||
->minValue(0)
|
||
->maxValue(365),
|
||
Toggle::make('rinnovo_automatico')
|
||
->label('Rinnovo automatico')
|
||
->default(false),
|
||
TextInput::make('importo_periodico')
|
||
->label('Importo periodico')
|
||
->numeric()
|
||
->step(0.01)
|
||
->minValue(0),
|
||
Select::make('documento_principale_id')
|
||
->label('Documento principale')
|
||
->options(fn(callable $get): array=> $this->getDocumentiContrattoOptions((int) ($get('stabile_id') ?: 0)))
|
||
->searchable()
|
||
->nullable(),
|
||
Repeater::make('codici_collegati')
|
||
->label('Codici collegati')
|
||
->schema([
|
||
TextInput::make('chiave')
|
||
->label('Chiave')
|
||
->placeholder('Es. POD, PDR, matricola_contatore, codice_cliente, utenza, matricola_ascensore')
|
||
->required(),
|
||
TextInput::make('valore')
|
||
->label('Valore')
|
||
->required(),
|
||
])
|
||
->addActionLabel('Aggiungi codice')
|
||
->defaultItems(0)
|
||
->columns(2),
|
||
Textarea::make('note')
|
||
->label('Note')
|
||
->rows(4),
|
||
];
|
||
}
|
||
|
||
protected function createContratto(array $data): void
|
||
{
|
||
$user = Auth::user();
|
||
if (! $user instanceof User) {
|
||
Notification::make()->title('Utente non valido')->danger()->send();
|
||
return;
|
||
}
|
||
|
||
$stabileId = (int) ($data['stabile_id'] ?? 0);
|
||
$stabile = StabileContext::accessibleStabili($user)->firstWhere('id', $stabileId);
|
||
if (! $stabile instanceof Stabile) {
|
||
Notification::make()->title('Stabile non valido')->danger()->send();
|
||
return;
|
||
}
|
||
|
||
$contract = StabileContratto::query()->create([
|
||
'stabile_id' => $stabileId,
|
||
'fornitore_id' => isset($data['fornitore_id']) && is_numeric($data['fornitore_id']) ? (int) $data['fornitore_id'] : null,
|
||
'documento_principale_id' => isset($data['documento_principale_id']) && is_numeric($data['documento_principale_id']) ? (int) $data['documento_principale_id'] : null,
|
||
'titolo' => trim((string) ($data['titolo'] ?? '')),
|
||
'tipo_contratto' => trim((string) ($data['tipo_contratto'] ?? 'altro')),
|
||
'categoria_impianto' => trim((string) ($data['tipo_contratto'] ?? 'altro')),
|
||
'servizio_label' => $this->nullableString($data['servizio_label'] ?? null),
|
||
'codice_contratto' => $this->nullableString($data['codice_contratto'] ?? null),
|
||
'riferimento_esterno' => $this->nullableString($data['riferimento_esterno'] ?? null),
|
||
'stato' => 'attivo',
|
||
'data_stipula' => $data['data_stipula'] ?? null,
|
||
'decorrenza_dal' => $data['decorrenza_dal'] ?? null,
|
||
'decorrenza_al' => $data['decorrenza_al'] ?? null,
|
||
'frequenza_scadenze' => $this->nullableString($data['frequenza_scadenze'] ?? null),
|
||
'giorno_scadenza' => isset($data['giorno_scadenza']) && is_numeric($data['giorno_scadenza']) ? (int) $data['giorno_scadenza'] : null,
|
||
'giorni_preavviso' => isset($data['giorni_preavviso']) && is_numeric($data['giorni_preavviso']) ? (int) $data['giorni_preavviso'] : 30,
|
||
'rinnovo_automatico' => (bool) ($data['rinnovo_automatico'] ?? false),
|
||
'importo_periodico' => isset($data['importo_periodico']) && is_numeric($data['importo_periodico']) ? (float) $data['importo_periodico'] : null,
|
||
'valuta' => 'EUR',
|
||
'codici_collegati' => $this->normalizeKeyValueRows($data['codici_collegati'] ?? []),
|
||
'dati_contratto' => [
|
||
'origine' => 'hub_contratti_stabile',
|
||
'documenti_url' => DocumentiArchivio::getUrl(panel: 'admin-filament'),
|
||
],
|
||
'note' => $this->nullableString($data['note'] ?? null),
|
||
'created_by' => (int) $user->id,
|
||
'updated_by' => (int) $user->id,
|
||
]);
|
||
|
||
if ((string) $contract->tipo_contratto === 'acqua') {
|
||
$servizio = StabileServizio::query()
|
||
->with('fornitore:id,ragione_sociale,nome,cognome')
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo', 'acqua')
|
||
->where(function ($query) use ($contract): void {
|
||
$query->where('codice_contratto', $contract->codice_contratto)
|
||
->orWhere('codice_utenza', $contract->riferimento_esterno)
|
||
->orWhere('codice_cliente', $contract->riferimento_esterno);
|
||
})
|
||
->orderByDesc('attivo')
|
||
->orderBy('id')
|
||
->first();
|
||
|
||
if ($servizio instanceof StabileServizio) {
|
||
$contract = app(AcquaContractSyncService::class)->syncForService($servizio, (int) $user->id) ?? $contract;
|
||
} else {
|
||
$this->syncScadenzeContratto($contract);
|
||
}
|
||
} else {
|
||
$this->syncScadenzeContratto($contract);
|
||
}
|
||
|
||
Notification::make()
|
||
->title('Contratto registrato')
|
||
->body('Scadenze programmate: ' . $contract->scadenze()->count())
|
||
->success()
|
||
->send();
|
||
}
|
||
|
||
protected function syncAcquaContract(): void
|
||
{
|
||
$user = Auth::user();
|
||
$stabileId = (int) ($this->stabileAttivo?->id ?? 0);
|
||
|
||
if (! $user instanceof User || $stabileId <= 0) {
|
||
Notification::make()->title('Stabile attivo non disponibile')->danger()->send();
|
||
return;
|
||
}
|
||
|
||
$stats = app(AcquaContractSyncService::class)->normalizeStabile($stabileId, (int) $user->id);
|
||
|
||
if (($stats['services'] ?? 0) <= 0) {
|
||
Notification::make()->title('Nessun servizio acqua configurato per lo stabile attivo')->warning()->send();
|
||
return;
|
||
}
|
||
|
||
Notification::make()
|
||
->title('Contratto acqua allineato')
|
||
->body('Servizi sincronizzati: ' . (int) ($stats['synced'] ?? 0) . '. Contratti acqua dopo bonifica: ' . (int) ($stats['contracts_after'] ?? 0) . '.')
|
||
->success()
|
||
->send();
|
||
}
|
||
|
||
protected function syncScadenzeContratto(StabileContratto $contract): void
|
||
{
|
||
$contract->scadenze()->delete();
|
||
|
||
foreach ($this->buildScheduleDates($contract) as $date) {
|
||
$popupDal = $date->copy()->subDays(max(0, (int) ($contract->giorni_preavviso ?? 0)));
|
||
|
||
Scadenza::query()->create([
|
||
'stabile_id' => (int) $contract->stabile_id,
|
||
'stabile_contratto_id' => (int) $contract->id,
|
||
'descrizione_sintetica' => $contract->titolo,
|
||
'data_scadenza' => $date->toDateString(),
|
||
'natura' => 'contratto_stabile',
|
||
'natura_label' => 'Scadenza contratto',
|
||
'gestione_tipo' => $contract->tipo_contratto,
|
||
'fatto' => 'N',
|
||
'tipo_riga' => trim((string) ($contract->servizio_label ?: $contract->tipo_contratto)),
|
||
'richiede_pop_up' => true,
|
||
'giorni_anticipo' => (int) ($contract->giorni_preavviso ?? 0),
|
||
'popup_dal' => $popupDal->toDateString(),
|
||
'legacy_payload' => [
|
||
'source' => 'stabile_contratti',
|
||
'codice_contratto' => $contract->codice_contratto,
|
||
'riferimento_esterno' => $contract->riferimento_esterno,
|
||
'codici_collegati' => $contract->codici_collegati ?? [],
|
||
],
|
||
]);
|
||
}
|
||
}
|
||
|
||
/** @return array<int, Carbon> */
|
||
protected function buildScheduleDates(StabileContratto $contract): array
|
||
{
|
||
$start = $contract->decorrenza_dal instanceof Carbon
|
||
? $contract->decorrenza_dal->copy()
|
||
: ($contract->decorrenza_al instanceof Carbon ? $contract->decorrenza_al->copy() : now()->copy());
|
||
$end = $contract->decorrenza_al instanceof Carbon ? $contract->decorrenza_al->copy() : null;
|
||
$frequency = trim((string) ($contract->frequenza_scadenze ?? 'una_tantum'));
|
||
|
||
if ($contract->giorno_scadenza) {
|
||
$start->day(min(28, (int) $contract->giorno_scadenza));
|
||
}
|
||
|
||
if ($frequency === '' || $frequency === 'una_tantum') {
|
||
return [$end ?: $start];
|
||
}
|
||
|
||
$dates = [];
|
||
$cursor = $start->copy();
|
||
$limit = $end
|
||
? $end->copy()
|
||
: ($contract->rinnovo_automatico ? $start->copy()->addMonths(24) : $start->copy()->addMonths(12));
|
||
|
||
while ($cursor <= $limit && count($dates) < 60) {
|
||
$dates[] = $cursor->copy();
|
||
$cursor = match ($frequency) {
|
||
'mensile' => $cursor->copy()->addMonthNoOverflow(),
|
||
'bimestrale' => $cursor->copy()->addMonthsNoOverflow(2),
|
||
'trimestrale' => $cursor->copy()->addMonthsNoOverflow(3),
|
||
'semestrale' => $cursor->copy()->addMonthsNoOverflow(6),
|
||
default => $cursor->copy()->addYearNoOverflow(),
|
||
};
|
||
|
||
if ($contract->giorno_scadenza) {
|
||
$cursor->day(min(28, (int) $contract->giorno_scadenza));
|
||
}
|
||
}
|
||
|
||
return $dates;
|
||
}
|
||
|
||
protected function getTipoContrattoOptions(): array
|
||
{
|
||
return [
|
||
'acqua' => 'Acqua',
|
||
'energia_elettrica' => 'Energia elettrica',
|
||
'ascensore' => 'Ascensore',
|
||
'gas' => 'Gas',
|
||
'pulizie' => 'Pulizie',
|
||
'portierato' => 'Portierato',
|
||
'assicurazione' => 'Assicurazione',
|
||
'antincendio' => 'Antincendio',
|
||
'raffrescamento' => 'Raffrescamento',
|
||
'riscaldamento' => 'Riscaldamento',
|
||
'telefonia_dati' => 'Telefonia / dati',
|
||
'altro' => 'Altro servizio',
|
||
];
|
||
}
|
||
|
||
protected function getFrequenzaOptions(): array
|
||
{
|
||
return [
|
||
'una_tantum' => 'Una tantum',
|
||
'mensile' => 'Mensile',
|
||
'bimestrale' => 'Bimestrale',
|
||
'trimestrale' => 'Trimestrale',
|
||
'semestrale' => 'Semestrale',
|
||
'annuale' => 'Annuale',
|
||
];
|
||
}
|
||
|
||
protected function getStabiliOptions(): array
|
||
{
|
||
$user = Auth::user();
|
||
if (! $user instanceof User) {
|
||
return [];
|
||
}
|
||
|
||
return StabileContext::accessibleStabili($user)
|
||
->mapWithKeys(fn(Stabile $stabile): array=> [(string) $stabile->id => trim((string) ($stabile->denominazione ?: ('Stabile #' . $stabile->id)))])
|
||
->all();
|
||
}
|
||
|
||
protected function getFornitoriOptions(): array
|
||
{
|
||
return Fornitore::query()
|
||
->orderByRaw("COALESCE(NULLIF(ragione_sociale, ''), CONCAT(COALESCE(nome, ''), ' ', COALESCE(cognome, ''))) asc")
|
||
->limit(1500)
|
||
->get(['id', 'ragione_sociale', 'nome', 'cognome'])
|
||
->mapWithKeys(function (Fornitore $fornitore): array {
|
||
$label = trim((string) ($fornitore->ragione_sociale ?: trim(($fornitore->nome ?? '') . ' ' . ($fornitore->cognome ?? ''))));
|
||
|
||
return [(string) $fornitore->id => ($label !== '' ? $label : ('Fornitore #' . $fornitore->id))];
|
||
})
|
||
->all();
|
||
}
|
||
|
||
protected function getDocumentiContrattoOptions(int $stabileId): array
|
||
{
|
||
if ($stabileId <= 0) {
|
||
return [];
|
||
}
|
||
|
||
return Documento::query()
|
||
->where('stabile_id', $stabileId)
|
||
->where('tipo_documento', 'contratto')
|
||
->orderByDesc('data_documento')
|
||
->limit(250)
|
||
->get(['id', 'nome', 'numero_protocollo'])
|
||
->mapWithKeys(fn(Documento $documento): array=> [
|
||
(string) $documento->id => trim((string) (($documento->numero_protocollo ? $documento->numero_protocollo . ' · ' : '') . ($documento->nome ?: ('Documento #' . $documento->id)))),
|
||
])
|
||
->all();
|
||
}
|
||
|
||
/** @param array<int, mixed> $rows */
|
||
protected function normalizeKeyValueRows(array $rows): array
|
||
{
|
||
return collect($rows)
|
||
->map(function ($row): ?array {
|
||
$key = trim((string) data_get($row, 'chiave', ''));
|
||
$value = trim((string) data_get($row, 'valore', ''));
|
||
|
||
return ($key !== '' && $value !== '') ? ['chiave' => $key, 'valore' => $value] : null;
|
||
})
|
||
->filter()
|
||
->values()
|
||
->all();
|
||
}
|
||
|
||
protected function nullableString(mixed $value): ?string
|
||
{
|
||
$value = trim((string) $value);
|
||
|
||
return $value !== '' ? $value : null;
|
||
}
|
||
|
||
public function contractBadgeColor(string $status): string
|
||
{
|
||
return match ($status) {
|
||
'attivo' => 'emerald',
|
||
'in_revisione' => 'amber',
|
||
'scaduto' => 'rose',
|
||
default => 'slate',
|
||
};
|
||
}
|
||
|
||
public function contractTypeLabel(?string $type): string
|
||
{
|
||
$key = trim((string) $type);
|
||
|
||
return $this->getTipoContrattoOptions()[$key] ?? ($key !== '' ? Str::headline($key) : 'Contratto');
|
||
}
|
||
}
|