, 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 string $insuranceBrokerSearch = ''; public string $insuranceCompanySearch = ''; /** @var array> */ public array $insuranceBrokerMatches = []; /** @var array> */ public array $insuranceCompanyMatches = []; /** @var array> */ public array $insurancePaymentRows = [ ['label' => '', 'due_date' => null, 'paid_at' => null, 'amount' => null, 'notes' => ''], ['label' => '', 'due_date' => null, 'paid_at' => null, 'amount' => null, 'notes' => ''], ]; /** @var array|null */ public ?array $insuranceAttachmentPreview = 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 updatedInsuranceBrokerSearch($value = null): void { $this->insuranceBrokerSearch = $this->normalizeInsuranceSearchValue($value ?? $this->insuranceBrokerSearch); $this->searchInsuranceRubrica('broker'); } public function updatedInsuranceCompanySearch($value = null): void { $this->insuranceCompanySearch = $this->normalizeInsuranceSearchValue($value ?? $this->insuranceCompanySearch); $this->searchInsuranceRubrica('company'); } public function updatedInsurancePolicyForm($value, string $key): void { if (in_array($key, ['broker_rubrica_id', 'company_rubrica_id'], true)) { $this->insurancePolicyForm[$key] = $this->normalizeInsuranceIdValue($value); } } public function selectInsurancePolicy($policyId): void { $policyId = $this->normalizeInsuranceIdValue($policyId); if ($policyId === null) { return; } $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 ?? ''), ]; $this->insuranceBrokerSearch = $this->formatRubricaLabel($policy->brokerRubrica); $this->insuranceCompanySearch = $this->formatRubricaLabel($policy->companyRubrica); $this->insuranceBrokerMatches = $policy->brokerRubrica ? [$this->mapInsuranceRubricaRow($policy->brokerRubrica)] : []; $this->insuranceCompanyMatches = $policy->companyRubrica ? [$this->mapInsuranceRubricaRow($policy->companyRubrica)] : []; $this->insurancePaymentRows = $this->buildInsurancePaymentRowsFromPolicy($policy); $this->insuranceAttachmentPreview = null; } 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; $this->insuranceBrokerSearch = ''; $this->insuranceCompanySearch = ''; $this->insuranceBrokerMatches = []; $this->insuranceCompanyMatches = []; $this->insurancePaymentRows = $this->defaultInsurancePaymentRows(); $this->insuranceAttachmentPreview = null; } public function selectInsuranceBroker($rubricaId): void { $rubricaId = $this->normalizeInsuranceIdValue($rubricaId); if ($rubricaId === null) { return; } $rubrica = RubricaUniversale::query()->find($rubricaId); if (! $rubrica instanceof RubricaUniversale) { return; } $this->insurancePolicyForm['broker_rubrica_id'] = (int) $rubrica->id; $this->insuranceBrokerSearch = $this->formatRubricaLabel($rubrica); $this->insuranceBrokerMatches = [$this->mapInsuranceRubricaRow($rubrica)]; } public function selectInsuranceCompany($rubricaId): void { $rubricaId = $this->normalizeInsuranceIdValue($rubricaId); if ($rubricaId === null) { return; } $rubrica = RubricaUniversale::query()->find($rubricaId); if (! $rubrica instanceof RubricaUniversale) { return; } $this->insurancePolicyForm['company_rubrica_id'] = (int) $rubrica->id; $this->insuranceCompanySearch = $this->formatRubricaLabel($rubrica); $this->insuranceCompanyMatches = [$this->mapInsuranceRubricaRow($rubrica)]; } public function resetInsuranceBrokerSelection(): void { $this->insurancePolicyForm['broker_rubrica_id'] = null; $this->insuranceBrokerSearch = ''; $this->insuranceBrokerMatches = []; } public function resetInsuranceCompanySelection(): void { $this->insurancePolicyForm['company_rubrica_id'] = null; $this->insuranceCompanySearch = ''; $this->insuranceCompanyMatches = []; } public function saveInsurancePolicy(): void { if (! $this->stabile instanceof StabileModel) { return; } $this->insurancePolicyForm['broker_rubrica_id'] = $this->normalizeInsuranceIdValue($this->insurancePolicyForm['broker_rubrica_id'] ?? null); $this->insurancePolicyForm['company_rubrica_id'] = $this->normalizeInsuranceIdValue($this->insurancePolicyForm['company_rubrica_id'] ?? null); $this->insuranceBrokerSearch = $this->normalizeInsuranceSearchValue($this->insuranceBrokerSearch); $this->insuranceCompanySearch = $this->normalizeInsuranceSearchValue($this->insuranceCompanySearch); $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(); $paymentRows = $this->normalizeInsurancePaymentRows($this->insurancePaymentRows); $selectedPolicyId = $this->normalizeInsuranceIdValue($this->selectedInsurancePolicyId); $policy = $selectedPolicyId ? InsurancePolicy::query()->where('stabile_id', (int) $this->stabile->id)->find($selectedPolicyId) : 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($this->serializeInsurancePaymentRows($paymentRows)), '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), 'metadata' => array_merge((array) ($policy->metadata ?? []), ['payment_rows' => $paymentRows]), '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($policyId): void { if (! $this->stabile instanceof StabileModel) { return; } $policyId = $this->normalizeInsuranceIdValue($policyId); if ($policyId === null) { 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($policy): ?string { if (! $policy instanceof InsurancePolicy) { return null; } $path = $this->normalizeInsuranceSearchValue($policy->policy_document_path ?? ''); $routeName = $this->resolveInsuranceAssetRouteName(); return $path !== '' && $routeName !== null ? route($routeName, ['policy' => (int) $policy->id, 'asset' => 'document']) : null; } public function getInsuranceSignatureUrl($policy): ?string { if (! $policy instanceof InsurancePolicy) { return null; } $path = $this->normalizeInsuranceSearchValue($policy->signature_image_path ?? ''); $routeName = $this->resolveInsuranceAssetRouteName(); return $path !== '' && $routeName !== null ? route($routeName, ['policy' => (int) $policy->id, 'asset' => 'signature']) : null; } public function getInsuranceTicketUrl($ticket): ?string { if (! $ticket instanceof Ticket) { return null; } return \App\Filament\Pages\Supporto\TicketGestione::getUrl(['ticket' => (int) $ticket->id, 'tab' => 'scheda'], panel: 'admin-filament'); } public function openInsuranceAttachmentPreview($policyId, string $asset): void { if (! in_array($asset, ['document', 'signature'], true)) { return; } $policyId = $this->normalizeInsuranceIdValue($policyId); if ($policyId === null) { return; } $policy = InsurancePolicy::query() ->with(['brokerRubrica', 'companyRubrica']) ->where('stabile_id', (int) ($this->stabile?->id ?? 0)) ->find($policyId); if (! $policy instanceof InsurancePolicy) { return; } $path = $asset === 'document' ? trim((string) ($policy->policy_document_path ?? '')) : trim((string) ($policy->signature_image_path ?? '')); if ($path === '') { return; } $mime = $asset === 'document' ? (string) ($policy->policy_document_mime ?: Storage::disk('public')->mimeType($path)) : (string) (Storage::disk('public')->mimeType($path) ?: 'image/png'); $resolvedMime = TicketAttachment::normalizeMimeType($mime, basename($path), $path); $url = $asset === 'document' ? $this->getInsuranceDocumentUrl($policy) : $this->getInsuranceSignatureUrl($policy); if (! is_string($url) || $url === '') { return; } $this->insuranceAttachmentPreview = [ 'name' => $asset === 'document' ? (string) ($policy->policy_document_name ?: ($policy->display_name . '.pdf')) : ('Firma ' . $policy->display_name), 'description' => $asset === 'document' ? 'Documento polizza archiviato con accesso controllato' : 'Firma referente polizza', 'url' => $url, 'is_image' => str_starts_with($resolvedMime, 'image/'), 'is_pdf' => $resolvedMime === 'application/pdf', ]; } public function closeInsuranceAttachmentPreview(): void { $this->insuranceAttachmentPreview = null; } 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->syncInsuranceDocumentArchive($policy); $this->insurancePolicyDocumentUpload = null; $this->insuranceSignatureUpload = null; } private function searchInsuranceRubrica(string $target): void { $field = $target === 'broker' ? 'insuranceBrokerSearch' : 'insuranceCompanySearch'; $matchField = $target === 'broker' ? 'insuranceBrokerMatches' : 'insuranceCompanyMatches'; $selectedField = $target === 'broker' ? 'broker_rubrica_id' : 'company_rubrica_id'; $raw = $this->normalizeInsuranceSearchValue($this->{$field} ?? ''); $this->{$field} = $raw; $this->{$matchField} = $this->searchRubricaLookupMatches( $raw, $this->normalizeInsuranceIdValue($this->insurancePolicyForm[$selectedField] ?? null), ['assicurazione', 'fornitore', 'altro'], 12 ); } /** * @return array */ private function mapInsuranceRubricaRow(RubricaUniversale $rubrica): array { return $this->mapRubricaLookupRow($rubrica); } private function formatRubricaLabel(?RubricaUniversale $rubrica): string { return $this->formatRubricaLookupLabel($rubrica); } private function normalizeInsuranceSearchValue($value): string { return $this->normalizeRubricaLookupText($value); } private function normalizeInsuranceIdValue($value): ?int { return $this->normalizeRubricaLookupId($value); } /** * @return array> */ private function defaultInsurancePaymentRows(): array { return [ ['label' => '', 'due_date' => null, 'paid_at' => null, 'amount' => null, 'notes' => ''], ['label' => '', 'due_date' => null, 'paid_at' => null, 'amount' => null, 'notes' => ''], ]; } /** * @return array> */ private function buildInsurancePaymentRowsFromPolicy(InsurancePolicy $policy): array { $rows = (array) data_get($policy->metadata, 'payment_rows', []); if ($rows === []) { $rows = $this->defaultInsurancePaymentRows(); $rows[0]['notes'] = (string) ($policy->payment_schedule_notes ?? ''); } return $this->normalizeInsurancePaymentRows($rows); } /** * @param array> $rows * @return array> */ private function normalizeInsurancePaymentRows(array $rows): array { $normalized = []; foreach (range(0, 1) as $index) { $row = (array) ($rows[$index] ?? []); $normalized[] = [ 'label' => trim((string) ($row['label'] ?? '')), 'due_date' => filled($row['due_date'] ?? null) ? (string) $row['due_date'] : null, 'paid_at' => filled($row['paid_at'] ?? null) ? (string) $row['paid_at'] : null, 'amount' => filled($row['amount'] ?? null) ? (float) $row['amount'] : null, 'notes' => trim((string) ($row['notes'] ?? '')), ]; } return $normalized; } /** * @param array> $rows */ private function serializeInsurancePaymentRows(array $rows): string { $lines = []; foreach ($rows as $index => $row) { if (! filled($row['label'] ?? null) && ! filled($row['amount'] ?? null) && ! filled($row['due_date'] ?? null) && ! filled($row['notes'] ?? null)) { continue; } $parts = ['Riga ' . ($index + 1)]; if (filled($row['label'] ?? null)) { $parts[] = trim((string) $row['label']); } if (filled($row['amount'] ?? null)) { $parts[] = number_format((float) $row['amount'], 2, ',', '.') . ' EUR'; } if (filled($row['due_date'] ?? null)) { $parts[] = 'Scadenza ' . \Carbon\Carbon::parse((string) $row['due_date'])->format('d/m/Y'); } if (filled($row['paid_at'] ?? null)) { $parts[] = 'Pagata ' . \Carbon\Carbon::parse((string) $row['paid_at'])->format('d/m/Y'); } if (filled($row['notes'] ?? null)) { $parts[] = trim((string) $row['notes']); } $lines[] = implode(' · ', $parts); } return implode("\n", $lines); } private function syncInsuranceDocumentArchive(InsurancePolicy $policy): void { $path = trim((string) ($policy->policy_document_path ?? '')); if ($path === '') { return; } $protocol = 'ASS-POL-' . (int) $policy->stabile_id . '-' . (int) $policy->id; $record = DocumentoCollegato::withTrashed()->firstOrNew([ 'numero_protocollo' => $protocol, ]); if ($record->trashed()) { $record->restore(); } $record->fill([ 'stabile_id' => (int) $policy->stabile_id, 'contatto_id' => (int) ($policy->company_rubrica_id ?: $policy->broker_rubrica_id ?: 0) ?: null, 'tipo_documento' => 'assicurazione', 'titolo' => $policy->display_name, 'descrizione' => $this->cleanInsuranceNullable($policy->coverage_summary) ?: 'Documento polizza assicurativa', 'data_sottoscrizione' => $policy->started_at, 'data_scadenza' => $policy->expires_at, 'tipo_contratto' => $this->mapInsuranceContractType($policy->payment_split), 'importo_contratto' => $policy->annual_amount, 'stato_documento' => $policy->status === 'scaduta' ? 'scaduto' : 'attivo', 'path_file_originale' => $path, 'note' => $policy->notes, 'creato_da' => $record->exists ? $record->creato_da : Auth::id(), 'modificato_da' => Auth::id(), ]); $record->save(); } private function mapInsuranceContractType(?string $paymentSplit): ?string { $value = mb_strtolower(trim((string) $paymentSplit)); return match (true) { str_contains($value, 'mens') => 'mensile', str_contains($value, 'bimes') => 'bimestrale', str_contains($value, 'trimes') => 'trimestrale', str_contains($value, 'semes') => 'semestrale', str_contains($value, 'ann') => 'annuale', $value !== '' => 'altro', default => null, }; } private function resolveInsuranceAssetRouteName(): ?string { foreach (['admin.insurance.policies.assets.view', 'superadmin.insurance.policies.assets.view'] as $routeName) { if (Route::has($routeName)) { return $routeName; } } return 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); } }