997 lines
39 KiB
PHP
997 lines
39 KiB
PHP
<?php
|
|
namespace App\Filament\Pages\Condomini;
|
|
|
|
use App\Models\DocumentoCollegato;
|
|
use App\Models\InsuranceClaim;
|
|
use App\Models\InsurancePolicy;
|
|
use App\Models\RubricaUniversale;
|
|
use App\Models\Stabile as StabileModel;
|
|
use App\Models\Ticket;
|
|
use App\Models\TicketAttachment;
|
|
use App\Models\User;
|
|
use App\Support\Livewire\SupportsRubricaLookup;
|
|
use App\Support\StabileContext;
|
|
use BackedEnum;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Livewire\WithFileUploads;
|
|
use UnitEnum;
|
|
|
|
class StabilePage extends Page
|
|
{
|
|
use WithFileUploads;
|
|
use SupportsRubricaLookup;
|
|
|
|
protected static ?string $navigationLabel = 'Stabile';
|
|
|
|
protected static ?string $title = 'Stabile';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-building-office-2';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'Stabile';
|
|
|
|
protected static ?int $navigationSort = 20;
|
|
|
|
protected static ?string $slug = 'condomini/stabile';
|
|
|
|
protected string $view = 'filament.pages.condomini.stabile';
|
|
|
|
public ?StabileModel $stabile = null;
|
|
|
|
public string $tab = 'dati-generali';
|
|
|
|
public bool $mostraRiscaldamento = false;
|
|
|
|
/** @var array{ordinaria: array<int>, riscaldamento: array<int>, straordinaria: array<int>} */
|
|
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<int> */
|
|
public array $gestioneMesi = [];
|
|
|
|
public ?int $gestioneNumeroRate = null;
|
|
|
|
public ?string $gestioneCadenza = null;
|
|
|
|
/** @var array<int, bool> */
|
|
public array $checklistFineAnnoState = [];
|
|
|
|
public ?int $selectedInsurancePolicyId = null;
|
|
|
|
public string $insuranceBrokerSearch = '';
|
|
|
|
public string $insuranceCompanySearch = '';
|
|
|
|
/** @var array<int,array<string,mixed>> */
|
|
public array $insuranceBrokerMatches = [];
|
|
|
|
/** @var array<int,array<string,mixed>> */
|
|
public array $insuranceCompanyMatches = [];
|
|
|
|
/** @var array<int,array<string,mixed>> */
|
|
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<string,mixed>|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<string,string>
|
|
*/
|
|
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<int, int|string> $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<int, InsurancePolicy>
|
|
*/
|
|
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<int, InsuranceClaim>
|
|
*/
|
|
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<int, string>
|
|
*/
|
|
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<string,mixed>
|
|
*/
|
|
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<int,array<string,mixed>>
|
|
*/
|
|
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<int,array<string,mixed>>
|
|
*/
|
|
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<int,array<string,mixed>> $rows
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
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<int,array<string,mixed>> $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);
|
|
}
|
|
}
|