, riscaldamento: array, straordinaria: array} */ public array $periodiEmissione = [ 'ordinaria' => [], 'riscaldamento' => [], 'straordinaria' => [], ]; public ?int $gestioneOrdinariaId = null; public ?int $gestioneSelezionataId = null; public ?string $gestioneSelezionataTipo = null; public ?string $gestioneDataInizio = null; public ?string $gestioneDataFine = null; /** @var array */ public array $gestioneMesi = []; public ?int $gestioneNumeroRate = null; public ?string $gestioneCadenza = null; /** @var array */ public array $checklistFineAnnoState = []; public ?int $selectedInsurancePolicyId = null; public array $insurancePolicyForm = [ 'broker_rubrica_id' => null, 'company_rubrica_id' => null, 'policy_name' => '', 'policy_number' => '', 'policy_reference' => '', 'status' => 'attiva', 'started_at' => null, 'expires_at' => null, 'renewal_at' => null, 'annual_amount' => null, 'payment_split' => '', 'payment_schedule_notes' => '', 'coverage_summary' => '', 'special_conditions' => '', 'claim_form_template' => '', 'notes' => '', ]; public $insurancePolicyDocumentUpload = null; public $insuranceSignatureUpload = 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) { abort(403); } $requestedStabileId = request()->integer('stabile_id'); if ($requestedStabileId > 0) { StabileContext::setActiveStabileId($user, (int) $requestedStabileId); } $activeId = StabileContext::resolveActiveStabileId($user); if (! $activeId) { $this->stabile = null; return; } $this->stabile = StabileContext::accessibleStabili($user)->firstWhere('id', $activeId); if (! $this->stabile) { $this->stabile = null; return; } $this->stabile->load([ 'amministratore', 'datiBancari', 'palazzine', 'documentiCollegati', ]); $config = (array) ($this->stabile->configurazione_avanzata ?? []); $this->gestioneOrdinariaId = isset($config['gestione_ordinaria_id']) ? (int) $config['gestione_ordinaria_id'] : null; $this->checklistFineAnnoState = is_array($config['checklist_fine_anno_state'] ?? null) ? $config['checklist_fine_anno_state'] : []; $periodi = (array) ($config['periodi_emissione'] ?? []); $fallbackOrdinaria = $this->normalizeMesi((array) ($this->stabile->rate_ordinarie_mesi ?? [])); if ($fallbackOrdinaria === []) { $fallbackOrdinaria = $this->extractLegacyRateMonthsFromConfig($config, 'ord'); } $fallbackRiscaldamento = $this->normalizeMesi((array) ($this->stabile->rate_riscaldamento_mesi ?? [])); if ($fallbackRiscaldamento === []) { $fallbackRiscaldamento = $this->extractLegacyRateMonthsFromConfig($config, 'ris'); } $this->periodiEmissione = [ 'ordinaria' => $this->normalizeMesi((array) ($periodi['ordinaria'] ?? [])) ?: $fallbackOrdinaria, 'riscaldamento' => $this->normalizeMesi((array) ($periodi['riscaldamento'] ?? [])) ?: $fallbackRiscaldamento, 'straordinaria' => $this->normalizeMesi((array) ($periodi['straordinaria'] ?? [])), ]; $this->mostraRiscaldamento = (bool) ($config['mostra_riscaldamento'] ?? ($this->periodiEmissione['riscaldamento'] !== [])); $gestioneSelezionataId = request()->integer('gestione_id'); if ($gestioneSelezionataId > 0) { $this->gestioneSelezionataId = $gestioneSelezionataId; if (! $this->gestioneOrdinariaId) { $this->gestioneOrdinariaId = $gestioneSelezionataId; } } $this->loadGestioneSelezionata(); $tab = request()->query('tab'); if (is_string($tab) && $tab !== '') { $this->tab = $tab; } if (! array_key_exists($this->tab, $this->tabs())) { $this->tab = 'dati-generali'; } } /** * @return array */ public function tabs(): array { return [ 'dati-generali' => 'Dati generali', 'unita-immobiliari' => 'Unità / Locali tecnici', 'palazzine' => 'Palazzine', 'assicurazioni' => 'Assicurazioni', 'tabelle-millesimali' => 'Tabelle millesimali', 'rate-emesse' => 'Rate emesse', 'gestioni' => 'Gestioni', 'gestione-documentale' => 'Gestione documentale', 'documenti-collegati' => 'Documenti collegati', 'dati-bancari' => 'Banche e casse', ]; } public function tabView(): string { return match ($this->tab) { 'rate-emesse' => 'admin.stabili.tabs.rate-emesse', 'palazzine' => 'admin.stabili.tabs.palazzine', 'assicurazioni' => 'filament.pages.condomini.tabs.assicurazioni', 'dati-bancari' => 'filament.pages.condomini.tabs.dati-bancari', 'tabelle-millesimali' => 'filament.pages.condomini.tabelle-millesimali-tab', 'gestioni' => 'admin.stabili.tabs.gestioni', 'unita-immobiliari' => 'admin.stabili.tabs.unita-immobiliari', 'gestione-documentale' => 'admin.stabili.tabs.gestione-documentale', 'documenti-collegati' => 'admin.stabili.tabs.documenti-collegati', default => 'admin.stabili.tabs.dati-generali', }; } public function toggleMostraRiscaldamento(): void { if (! $this->stabile) { return; } $config = (array) ($this->stabile->configurazione_avanzata ?? []); $value = ! (bool) ($config['mostra_riscaldamento'] ?? false); $config['mostra_riscaldamento'] = $value; $this->stabile->configurazione_avanzata = $config; $this->stabile->save(); $this->mostraRiscaldamento = $value; $this->dispatch('refresh-gestioni-stabile'); } public function savePeriodiEmissione(): void { if (! $this->stabile) { return; } $config = (array) ($this->stabile->configurazione_avanzata ?? []); $config['periodi_emissione'] = [ 'ordinaria' => $this->normalizeMesi((array) ($this->periodiEmissione['ordinaria'] ?? [])), 'riscaldamento' => $this->normalizeMesi((array) ($this->periodiEmissione['riscaldamento'] ?? [])), 'straordinaria' => $this->normalizeMesi((array) ($this->periodiEmissione['straordinaria'] ?? [])), ]; $this->stabile->configurazione_avanzata = $config; $this->stabile->save(); $this->periodiEmissione = $config['periodi_emissione']; Notification::make() ->title('Periodi emissione rate salvati') ->success() ->send(); } public function saveGestioneSelezionata(): void { if (! $this->stabile || ! $this->gestioneSelezionataId) { return; } $gestione = \App\Models\GestioneContabile::query()->find($this->gestioneSelezionataId); if (! $gestione) { return; } $gestione->data_inizio = $this->gestioneDataInizio ?: null; $gestione->data_fine = $this->gestioneDataFine ?: null; $mesi = $this->normalizeMesi($this->gestioneMesi); $field = match ($gestione->tipo_gestione) { 'ordinaria' => 'mesi_rate_ordinaria', 'riscaldamento' => 'mesi_rate_riscaldamento', 'straordinaria' => 'mesi_rate_straordinaria', default => 'mesi_rate_ordinaria', }; $gestione->{$field} = $mesi; if ($gestione->tipo_gestione === 'straordinaria') { $piano = (array) ($gestione->piano_straordinario ?? []); $piano['numero_rate'] = $this->gestioneNumeroRate; $piano['cadenza'] = $this->gestioneCadenza; $gestione->piano_straordinario = $piano; } $gestione->save(); Notification::make() ->title('Gestione aggiornata') ->success() ->send(); } public function saveChecklistFineAnno(): void { if (! $this->stabile) { return; } $config = (array) ($this->stabile->configurazione_avanzata ?? []); $config['gestione_ordinaria_id'] = $this->gestioneOrdinariaId; $config['checklist_fine_anno_state'] = $this->checklistFineAnnoState; $this->stabile->configurazione_avanzata = $config; $this->stabile->save(); Notification::make() ->title('Checklist salvata') ->success() ->send(); } /** * Carica la gestione selezionata (o quella ordinaria di riferimento) * e popola i campi di editing della tab periodi. */ private function loadGestioneSelezionata(): void { $gestioneId = $this->gestioneSelezionataId ?? $this->gestioneOrdinariaId; if (! $gestioneId) { return; } $gestione = \App\Models\GestioneContabile::query()->find($gestioneId); if (! $gestione) { return; } $this->gestioneSelezionataId = $gestione->id; $this->gestioneSelezionataTipo = $gestione->tipo_gestione; $this->gestioneDataInizio = $gestione->data_inizio?->format('Y-m-d'); $this->gestioneDataFine = $gestione->data_fine?->format('Y-m-d'); $mesi = match ($gestione->tipo_gestione) { 'ordinaria' => (array) ($gestione->mesi_rate_ordinaria ?? []), 'riscaldamento' => (array) ($gestione->mesi_rate_riscaldamento ?? []), 'straordinaria' => (array) ($gestione->mesi_rate_straordinaria ?? []), default => [], }; $this->gestioneMesi = $this->normalizeMesi($mesi); $piano = (array) ($gestione->piano_straordinario ?? []); $this->gestioneNumeroRate = isset($piano['numero_rate']) ? (int) $piano['numero_rate'] : null; $this->gestioneCadenza = $piano['cadenza'] ?? null; } /** @param array $mesi */ private function normalizeMesi(array $mesi): array { $mesi = array_values(array_unique(array_map('intval', $mesi))); sort($mesi); return $mesi; } /** * @return \Illuminate\Database\Eloquent\Collection */ public function getInsurancePoliciesProperty() { if (! $this->stabile instanceof StabileModel) { return InsurancePolicy::query()->whereRaw('1 = 0')->get(); } return InsurancePolicy::query() ->with(['brokerRubrica', 'companyRubrica']) ->where('stabile_id', (int) $this->stabile->id) ->orderByDesc('renewal_at') ->orderByDesc('expires_at') ->orderByDesc('id') ->get(); } /** * @return \Illuminate\Database\Eloquent\Collection */ public function getInsuranceClaimsRowsProperty() { if (! $this->stabile instanceof StabileModel) { return InsuranceClaim::query()->whereRaw('1 = 0')->get(); } return InsuranceClaim::query() ->with(['ticket', 'insurancePolicy']) ->where('stabile_id', (int) $this->stabile->id) ->when( (int) ($this->selectedInsurancePolicyId ?? 0) > 0, fn($query) => $query->where('insurance_policy_id', (int) $this->selectedInsurancePolicyId) ) ->latest('opened_at') ->latest('id') ->get(); } /** * @return array */ public function getInsuranceRubricaOptionsProperty(): array { return RubricaUniversale::query() ->whereNull('deleted_at') ->whereIn('categoria', ['assicurazione', 'fornitore', 'altro']) ->orderBy('ragione_sociale') ->orderBy('cognome') ->orderBy('nome') ->limit(300) ->get(['id', 'ragione_sociale', 'nome', 'cognome']) ->mapWithKeys(function (RubricaUniversale $rubrica): array { $label = trim((string) ($rubrica->ragione_sociale ?: trim(($rubrica->nome ?? '') . ' ' . ($rubrica->cognome ?? '')))); return [(int) $rubrica->id => ($label !== '' ? $label : ('Rubrica #' . (int) $rubrica->id))]; }) ->all(); } public function selectInsurancePolicy(int $policyId): void { $policy = $this->insurancePolicies->firstWhere('id', $policyId); if (! $policy instanceof InsurancePolicy) { return; } $this->selectedInsurancePolicyId = (int) $policy->id; $this->insurancePolicyForm = [ 'broker_rubrica_id' => $policy->broker_rubrica_id, 'company_rubrica_id' => $policy->company_rubrica_id, 'policy_name' => (string) ($policy->policy_name ?? ''), 'policy_number' => (string) ($policy->policy_number ?? ''), 'policy_reference' => (string) ($policy->policy_reference ?? ''), 'status' => (string) ($policy->status ?? 'attiva'), 'started_at' => optional($policy->started_at)->format('Y-m-d'), 'expires_at' => optional($policy->expires_at)->format('Y-m-d'), 'renewal_at' => optional($policy->renewal_at)->format('Y-m-d'), 'annual_amount' => $policy->annual_amount, 'payment_split' => (string) ($policy->payment_split ?? ''), 'payment_schedule_notes' => (string) ($policy->payment_schedule_notes ?? ''), 'coverage_summary' => (string) ($policy->coverage_summary ?? ''), 'special_conditions' => (string) ($policy->special_conditions ?? ''), 'claim_form_template' => (string) ($policy->claim_form_template ?? ''), 'notes' => (string) ($policy->notes ?? ''), ]; } public function resetInsurancePolicyForm(): void { $this->selectedInsurancePolicyId = null; $this->insurancePolicyForm = [ 'broker_rubrica_id' => null, 'company_rubrica_id' => null, 'policy_name' => '', 'policy_number' => '', 'policy_reference' => '', 'status' => 'attiva', 'started_at' => null, 'expires_at' => null, 'renewal_at' => null, 'annual_amount' => null, 'payment_split' => '', 'payment_schedule_notes' => '', 'coverage_summary' => '', 'special_conditions' => '', 'claim_form_template' => '', 'notes' => '', ]; $this->insurancePolicyDocumentUpload = null; $this->insuranceSignatureUpload = null; } public function saveInsurancePolicy(): void { if (! $this->stabile instanceof StabileModel) { return; } $data = validator($this->insurancePolicyForm, [ 'broker_rubrica_id' => ['nullable', 'integer', 'exists:rubrica_universale,id'], 'company_rubrica_id' => ['nullable', 'integer', 'exists:rubrica_universale,id'], 'policy_name' => ['nullable', 'string', 'max:255'], 'policy_number' => ['nullable', 'string', 'max:255'], 'policy_reference' => ['nullable', 'string', 'max:255'], 'status' => ['required', 'string', 'max:50'], 'started_at' => ['nullable', 'date'], 'expires_at' => ['nullable', 'date'], 'renewal_at' => ['nullable', 'date'], 'annual_amount' => ['nullable', 'numeric'], 'payment_split' => ['nullable', 'string', 'max:255'], 'payment_schedule_notes' => ['nullable', 'string', 'max:5000'], 'coverage_summary' => ['nullable', 'string', 'max:10000'], 'special_conditions' => ['nullable', 'string', 'max:20000'], 'claim_form_template' => ['nullable', 'string', 'max:20000'], 'notes' => ['nullable', 'string', 'max:10000'], ])->validate(); $policy = $this->selectedInsurancePolicyId ? InsurancePolicy::query()->where('stabile_id', (int) $this->stabile->id)->find($this->selectedInsurancePolicyId) : new InsurancePolicy(); if (! $policy instanceof InsurancePolicy) { $policy = new InsurancePolicy(); } $policy->fill([ 'stabile_id' => (int) $this->stabile->id, 'broker_rubrica_id' => (int) ($data['broker_rubrica_id'] ?? 0) ?: null, 'company_rubrica_id' => (int) ($data['company_rubrica_id'] ?? 0) ?: null, 'policy_name' => $this->cleanInsuranceNullable($data['policy_name'] ?? null), 'policy_number' => $this->cleanInsuranceNullable($data['policy_number'] ?? null), 'policy_reference' => $this->cleanInsuranceNullable($data['policy_reference'] ?? null), 'status' => $this->cleanInsuranceNullable($data['status'] ?? 'attiva') ?: 'attiva', 'started_at' => $data['started_at'] ?? null, 'expires_at' => $data['expires_at'] ?? null, 'renewal_at' => $data['renewal_at'] ?? null, 'annual_amount' => $data['annual_amount'] ?? null, 'payment_split' => $this->cleanInsuranceNullable($data['payment_split'] ?? null), 'payment_schedule_notes' => $this->cleanInsuranceNullable($data['payment_schedule_notes'] ?? null), 'coverage_summary' => $this->cleanInsuranceNullable($data['coverage_summary'] ?? null), 'special_conditions' => $this->cleanInsuranceNullable($data['special_conditions'] ?? null), 'claim_form_template' => $this->cleanInsuranceNullable($data['claim_form_template'] ?? null), 'notes' => $this->cleanInsuranceNullable($data['notes'] ?? null), 'created_by' => Auth::id(), ]); $policy->save(); $this->storeInsuranceUploads($policy); $this->selectedInsurancePolicyId = (int) $policy->id; Notification::make()->title('Polizza assicurativa salvata')->success()->send(); } public function deleteInsurancePolicy(int $policyId): void { if (! $this->stabile instanceof StabileModel) { return; } $policy = InsurancePolicy::query()->where('stabile_id', (int) $this->stabile->id)->find($policyId); if (! $policy instanceof InsurancePolicy) { return; } $policy->delete(); if ((int) ($this->selectedInsurancePolicyId ?? 0) === (int) $policyId) { $this->resetInsurancePolicyForm(); } Notification::make()->title('Polizza assicurativa eliminata')->success()->send(); } public function getInsuranceDocumentUrl(InsurancePolicy $policy): ?string { $path = trim((string) ($policy->policy_document_path ?? '')); return $path !== '' ? Storage::disk('public')->url($path) : null; } public function getInsuranceSignatureUrl(InsurancePolicy $policy): ?string { $path = trim((string) ($policy->signature_image_path ?? '')); return $path !== '' ? Storage::disk('public')->url($path) : null; } public function getInsuranceTicketUrl(?Ticket $ticket): ?string { if (! $ticket instanceof Ticket) { return null; } return \App\Filament\Pages\Supporto\TicketGestione::getUrl(['ticket' => (int) $ticket->id, 'tab' => 'scheda'], panel: 'admin-filament'); } private function storeInsuranceUploads(InsurancePolicy $policy): void { if (is_object($this->insurancePolicyDocumentUpload) && method_exists($this->insurancePolicyDocumentUpload, 'storeAs')) { $ext = strtolower((string) ($this->insurancePolicyDocumentUpload->getClientOriginalExtension() ?: 'pdf')); $fileName = 'stabile-' . (int) $policy->stabile_id . '-polizza-' . (int) $policy->id . '-documento.' . $ext; $path = $this->insurancePolicyDocumentUpload->storeAs('assicurazioni/polizze', $fileName, 'public'); $policy->policy_document_path = $path; $policy->policy_document_name = (string) $this->insurancePolicyDocumentUpload->getClientOriginalName(); $policy->policy_document_mime = (string) $this->insurancePolicyDocumentUpload->getClientMimeType(); } if (is_object($this->insuranceSignatureUpload) && method_exists($this->insuranceSignatureUpload, 'storeAs')) { $ext = strtolower((string) ($this->insuranceSignatureUpload->getClientOriginalExtension() ?: 'png')); $fileName = 'stabile-' . (int) $policy->stabile_id . '-polizza-' . (int) $policy->id . '-firma.' . $ext; $policy->signature_image_path = $this->insuranceSignatureUpload->storeAs('assicurazioni/firme', $fileName, 'public'); } $policy->save(); $this->insurancePolicyDocumentUpload = null; $this->insuranceSignatureUpload = null; } private function cleanInsuranceNullable($value): ?string { $value = trim((string) $value); return $value !== '' ? $value : null; } private function extractLegacyRateMonthsFromConfig(array $config, string $section): array { $flags = (array) data_get($config, 'rata_flags.' . $section, []); $months = []; foreach (range(1, 12) as $month) { $value = $flags[$section . '_rata_' . $month] ?? null; if ($value === null) { continue; } $normalized = strtolower(trim((string) $value)); if ($normalized !== '' && ! in_array($normalized, ['0', 'false', 'no', 'off', 'n'], true)) { $months[] = $month; } } return $this->normalizeMesi($months); } }