738 lines
57 KiB
PHP
Executable File
738 lines
57 KiB
PHP
Executable File
<x-filament-panels::page>
|
||
<div class="mx-auto max-w-7xl space-y-4">
|
||
<x-filament.components.page-breadcrumbs
|
||
:breadcrumbs="[
|
||
['label' => 'Contabilità', 'url' => null],
|
||
['label' => 'Casse e banche', 'url' => null],
|
||
]"
|
||
/>
|
||
|
||
@php
|
||
$stabile = $this->getActiveStabile();
|
||
$contiRows = $this->getHubContiRows();
|
||
$contiOptions = $this->getContiImportabili();
|
||
$hdr = $this->getHeaderSaldoInfo();
|
||
$periodo = $this->periodoTotali;
|
||
$periodoOptions = $this->periodoOptions;
|
||
$periodoRateOptions = $this->periodoRateOptions;
|
||
$periodoRate = $this->periodoRateTotali;
|
||
$contoImport = $this->getSelectedContoImportInfo();
|
||
$snapshotCards = $this->getSaldoSnapshotCards();
|
||
$saldoArchivio = $this->getSaldoArchivioByYear();
|
||
$ricStats = $this->getRiconciliazioneStats();
|
||
$ricQueue = $this->getRiconciliazioneQueue();
|
||
$ricCurrent = $this->getRiconciliazioneCurrent();
|
||
$ricCandidates = $this->getRiconciliazioneCandidates();
|
||
$archiveSummary = $this->getArchivioOperativoSummary();
|
||
$gestioniRilevate = $this->getGestioniContabiliRilevate();
|
||
$ricBuckets = $this->getRiconciliazioneBucketRows();
|
||
$tipoEstrattoOptions = [
|
||
'estratto_conto' => 'Estratto conto',
|
||
'saldo_banca' => 'Saldo banca',
|
||
'riconciliazione' => 'Riconciliazione',
|
||
'altro' => 'Altro',
|
||
];
|
||
@endphp
|
||
|
||
@if(! $stabile)
|
||
<x-filament::section>
|
||
<div class="text-sm text-gray-600">Seleziona uno stabile per importare e verificare i movimenti banca.</div>
|
||
</x-filament::section>
|
||
@else
|
||
<x-filament::section>
|
||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,1.4fr)_minmax(320px,0.8fr)]">
|
||
<div>
|
||
<div class="text-sm text-gray-500">Stabile attivo</div>
|
||
<div class="text-lg font-semibold text-gray-900">
|
||
{{ $stabile->codice_operatore ?? $stabile->codice_stabile ?? '—' }} — {{ $stabile->denominazione ?? 'Stabile' }}
|
||
</div>
|
||
<div class="mt-1 text-xs text-gray-600">Hub operativo del conto corrente: conti, saldi ufficiali, movimenti importati e riconciliazione.</div>
|
||
</div>
|
||
|
||
<div class="rounded-xl border bg-slate-50 p-4">
|
||
<div class="text-xs font-medium uppercase tracking-wide text-slate-500">Conto attivo</div>
|
||
<div class="mt-2">
|
||
<select wire:model.live="contoId" class="w-full rounded-lg border-gray-300 text-sm">
|
||
<option value="">Seleziona conto</option>
|
||
@foreach($contiOptions as $conto)
|
||
<option value="{{ $conto['id'] }}">{{ $conto['label'] }}</option>
|
||
@endforeach
|
||
</select>
|
||
</div>
|
||
<div class="mt-3 text-sm text-slate-700">
|
||
@if($contoImport)
|
||
<div class="font-medium">{{ $contoImport['label'] }}</div>
|
||
@else
|
||
<div>Nessun conto selezionato.</div>
|
||
@endif
|
||
</div>
|
||
<div class="mt-3 flex flex-wrap gap-2">
|
||
<x-filament::button size="sm" color="primary" type="button" wire:click="mountAction('importa_estratto_unificato')">
|
||
Importa estratto
|
||
</x-filament::button>
|
||
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('registra_saldo_estratto')">
|
||
Registra saldo
|
||
</x-filament::button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</x-filament::section>
|
||
|
||
<x-filament::tabs>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'conti'" wire:click="goToHubTab('conti')">Conti</x-filament::tabs.item>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'saldi'" wire:click="goToHubTab('saldi')">Saldi e estratti</x-filament::tabs.item>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'movimenti'" wire:click="goToHubTab('movimenti')">Movimenti</x-filament::tabs.item>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'struttura'" wire:click="goToHubTab('struttura')">Struttura e gestioni</x-filament::tabs.item>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'riconciliazione'" wire:click="goToHubTab('riconciliazione')">Riconciliazione</x-filament::tabs.item>
|
||
<x-filament::tabs.item :active="$this->hubTab === 'associazione_causali'" wire:click="goToHubTab('associazione_causali')">Associazione Causali</x-filament::tabs.item>
|
||
</x-filament::tabs>
|
||
|
||
@if($this->hubTab === 'conti')
|
||
<x-filament::section>
|
||
<x-slot name="heading">Conti dello stabile</x-slot>
|
||
<x-slot name="description">Vista generale equivalente a “Casse e banche”, ma dentro l'hub operativo del conto corrente.</x-slot>
|
||
|
||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||
@forelse($contiRows as $row)
|
||
<div @class([
|
||
'rounded-xl border p-4',
|
||
'border-primary-300 bg-primary-50/40' => $row['selected'],
|
||
'bg-white' => ! $row['selected'],
|
||
])>
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div>
|
||
<div class="text-sm font-semibold text-slate-900">{{ $row['label'] }}</div>
|
||
<div class="mt-1 text-xs text-slate-600">
|
||
@if($row['snapshot_data'])
|
||
Ultimo saldo ufficiale: {{ $row['snapshot_data'] }}
|
||
@if($row['snapshot_tipo'])
|
||
· {{ $row['snapshot_tipo'] }}
|
||
@endif
|
||
@else
|
||
Nessun saldo ufficiale registrato.
|
||
@endif
|
||
</div>
|
||
</div>
|
||
@if($row['selected'])
|
||
<span class="rounded-full bg-primary-600 px-2.5 py-1 text-[11px] font-semibold text-white">Attivo</span>
|
||
@endif
|
||
</div>
|
||
|
||
<div class="mt-4 grid grid-cols-2 gap-3 text-sm">
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Saldo attuale</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">€ {{ number_format((float) ($row['saldo_attuale'] ?? 0), 2, ',', '.') }}</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Movimenti importati</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">{{ $row['movimenti_count'] ?? 0 }}</div>
|
||
<div class="text-xs text-slate-500">Ultima data: {{ $row['ultima_movimentazione'] ?? '—' }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4 flex flex-wrap gap-2">
|
||
<x-filament::button size="sm" color="gray" wire:click="selectContoAndTab({{ $row['id'] }}, 'saldi')">Saldi / estratti</x-filament::button>
|
||
<x-filament::button size="sm" color="primary" wire:click="selectContoAndTab({{ $row['id'] }}, 'movimenti')">Movimenti</x-filament::button>
|
||
<x-filament::button size="sm" color="success" wire:click="selectContoAndTab({{ $row['id'] }}, 'riconciliazione')">Riconciliazione</x-filament::button>
|
||
</div>
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed bg-white p-4 text-sm text-slate-500 lg:col-span-2">Nessun conto configurato per lo stabile attivo.</div>
|
||
@endforelse
|
||
</div>
|
||
</x-filament::section>
|
||
@elseif($this->hubTab === 'saldi')
|
||
<x-filament::section>
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div>
|
||
<div class="text-lg font-semibold text-slate-900">Archivio saldi ufficiali ed estratti conto</div>
|
||
<div class="mt-1 text-sm text-slate-600">Vista annuale del conto selezionato, separata dal mastrino dei movimenti.</div>
|
||
@if($contoImport)
|
||
<div class="mt-2 text-xs font-medium text-slate-700">Conto: {{ $contoImport['label'] }}</div>
|
||
@endif
|
||
</div>
|
||
<x-filament::button type="button" color="primary" wire:click="mountAction('registra_saldo_estratto')">
|
||
Nuovo saldo / estratto
|
||
</x-filament::button>
|
||
</div>
|
||
|
||
<div class="mt-4 space-y-6">
|
||
@forelse($saldoArchivio as $yearBlock)
|
||
<div class="rounded-xl border bg-white p-4">
|
||
<div class="flex items-center justify-between gap-3">
|
||
<div class="text-base font-semibold text-slate-900">Anno {{ $yearBlock['year'] }}</div>
|
||
<div class="text-xs text-slate-500">{{ count($yearBlock['rows']) }} estratti / saldi</div>
|
||
</div>
|
||
|
||
<div class="mt-4 overflow-x-auto">
|
||
<table class="min-w-full text-sm">
|
||
<thead class="text-left text-slate-500">
|
||
<tr>
|
||
<th class="py-2 pr-4">Data saldo</th>
|
||
<th class="py-2 pr-4">Periodo</th>
|
||
<th class="py-2 pr-4">Esito</th>
|
||
<th class="py-2 pr-4">Tipo</th>
|
||
<th class="py-2 pr-4 text-right">Saldo</th>
|
||
<th class="py-2 pr-4">Documento</th>
|
||
<th class="py-2 pr-4">Note</th>
|
||
<th class="py-2 pr-0 text-right">Azioni</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y">
|
||
@foreach($yearBlock['rows'] as $row)
|
||
<tr>
|
||
<td class="py-3 pr-4 font-medium text-slate-900">{{ $row['data'] }}</td>
|
||
<td class="py-3 pr-4">
|
||
<div>{{ $row['periodo_breve'] }}</div>
|
||
<div class="text-xs text-slate-500">{{ $row['periodo'] }}</div>
|
||
</td>
|
||
<td class="py-3 pr-4 align-top">
|
||
@if(($row['quadratura_status'] ?? 'neutral') === 'ok')
|
||
<div class="inline-flex items-center gap-1 rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-semibold text-emerald-700">
|
||
<x-heroicon-o-check-circle class="h-4 w-4" />
|
||
OK
|
||
</div>
|
||
@elseif(($row['quadratura_status'] ?? 'neutral') === 'anchor')
|
||
<div class="inline-flex items-center gap-1 rounded-full bg-sky-100 px-2.5 py-1 text-xs font-semibold text-sky-700">
|
||
<x-heroicon-o-flag class="h-4 w-4" />
|
||
Partenza
|
||
</div>
|
||
@elseif(($row['quadratura_status'] ?? 'neutral') === 'mismatch')
|
||
<div class="inline-flex items-center gap-1 rounded-full bg-amber-100 px-2.5 py-1 text-xs font-semibold text-amber-700">
|
||
<x-heroicon-o-exclamation-triangle class="h-4 w-4" />
|
||
Verifica
|
||
</div>
|
||
@else
|
||
<div class="inline-flex items-center gap-1 rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-600">
|
||
<x-heroicon-o-minus-circle class="h-4 w-4" />
|
||
N/D
|
||
</div>
|
||
@endif
|
||
<div class="mt-1 text-xs text-slate-500">{{ $row['quadratura_label'] ?? '—' }}</div>
|
||
@if(($row['quadratura_status'] ?? null) === 'mismatch' && array_key_exists('quadratura_delta', $row))
|
||
<div class="mt-1 text-xs font-medium text-rose-700">
|
||
Scarto: € {{ number_format((float) ($row['quadratura_delta'] ?? 0), 2, ',', '.') }}
|
||
</div>
|
||
@endif
|
||
</td>
|
||
<td class="py-3 pr-4">{{ $row['tipo'] }}</td>
|
||
<td class="py-3 pr-4 text-right font-semibold">€ {{ number_format((float) $row['saldo'], 2, ',', '.') }}</td>
|
||
<td class="py-3 pr-4">
|
||
@if($row['documento_url'] && $row['documento_label'])
|
||
<button type="button" wire:click="openSaldoDocumento({{ $row['id'] }})" class="text-primary-600 hover:underline">{{ $row['documento_label'] }}</button>
|
||
@else
|
||
<span class="text-slate-400">—</span>
|
||
@endif
|
||
</td>
|
||
<td class="py-3 pr-4 text-slate-600">{{ $row['note'] !== '' ? $row['note'] : '—' }}</td>
|
||
<td class="py-3 pr-0 text-right">
|
||
<div class="flex flex-wrap justify-end gap-2">
|
||
<x-filament::button size="sm" color="gray" wire:click="openSaldoPeriodo({{ $row['id'] }})">Apri movimenti</x-filament::button>
|
||
<x-filament::button size="sm" color="warning" wire:click="startEditingSaldo({{ $row['id'] }})">Modifica</x-filament::button>
|
||
@if(($this->confirmingDeleteSaldoId ?? null) === $row['id'])
|
||
<x-filament::button size="sm" color="danger" wire:click="deleteSaldo({{ $row['id'] }})">Conferma elimina</x-filament::button>
|
||
<x-filament::button size="sm" color="gray" wire:click="cancelDeleteSaldo">Annulla</x-filament::button>
|
||
@else
|
||
<x-filament::button size="sm" color="danger" wire:click="requestDeleteSaldo({{ $row['id'] }})">Elimina</x-filament::button>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@if(($this->editingSaldoId ?? null) === $row['id'])
|
||
<tr class="bg-amber-50/60">
|
||
<td colspan="8" class="py-4 pr-0">
|
||
<div class="rounded-xl border border-amber-200 bg-white p-4">
|
||
<div class="text-sm font-semibold text-slate-900">Modifica saldo / estratto</div>
|
||
<div class="mt-1 text-xs text-slate-600">Puoi correggere data saldo, periodo, tipo e note del record gia memorizzato.</div>
|
||
|
||
<div class="mt-4 grid grid-cols-1 gap-3 lg:grid-cols-3">
|
||
<label class="text-xs text-slate-600">
|
||
<span class="mb-1 block">Data saldo</span>
|
||
<input type="date" wire:model.defer="editingSaldoData.data_saldo" class="w-full rounded-lg border-gray-300 text-sm" />
|
||
</label>
|
||
<label class="text-xs text-slate-600">
|
||
<span class="mb-1 block">Periodo dal</span>
|
||
<input type="date" wire:model.defer="editingSaldoData.periodo_da" class="w-full rounded-lg border-gray-300 text-sm" />
|
||
</label>
|
||
<label class="text-xs text-slate-600">
|
||
<span class="mb-1 block">Periodo al</span>
|
||
<input type="date" wire:model.defer="editingSaldoData.periodo_a" class="w-full rounded-lg border-gray-300 text-sm" />
|
||
</label>
|
||
<label class="text-xs text-slate-600">
|
||
<span class="mb-1 block">Saldo ufficiale</span>
|
||
<input type="text" wire:model.defer="editingSaldoData.saldo" class="w-full rounded-lg border-gray-300 text-sm" />
|
||
</label>
|
||
<label class="text-xs text-slate-600">
|
||
<span class="mb-1 block">Tipo riferimento</span>
|
||
<select wire:model.defer="editingSaldoData.tipo_estratto" class="w-full rounded-lg border-gray-300 text-sm">
|
||
<option value="">Saldo registrato</option>
|
||
@foreach($tipoEstrattoOptions as $tipoValue => $tipoLabel)
|
||
<option value="{{ $tipoValue }}">{{ $tipoLabel }}</option>
|
||
@endforeach
|
||
</select>
|
||
</label>
|
||
<label class="flex items-center gap-2 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-700">
|
||
<input type="checkbox" wire:model.defer="editingSaldoData.is_partenza_contabile" class="rounded border-gray-300 text-primary-600" />
|
||
<span>Usa questo saldo come partenza contabile</span>
|
||
</label>
|
||
<label class="text-xs text-slate-600 lg:col-span-2">
|
||
<span class="mb-1 block">Note</span>
|
||
<input type="text" wire:model.defer="editingSaldoData.note" class="w-full rounded-lg border-gray-300 text-sm" />
|
||
</label>
|
||
</div>
|
||
|
||
<div class="mt-4 flex flex-wrap justify-end gap-2">
|
||
<x-filament::button size="sm" color="gray" wire:click="cancelEditingSaldo">Annulla</x-filament::button>
|
||
<x-filament::button size="sm" color="primary" wire:click="saveEditingSaldo">Salva modifiche</x-filament::button>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endif
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed bg-white p-4 text-sm text-slate-500">Nessun saldo ufficiale registrato per il conto selezionato.</div>
|
||
@endforelse
|
||
</div>
|
||
</x-filament::section>
|
||
@elseif($this->hubTab === 'struttura')
|
||
<x-filament::section>
|
||
<x-slot name="heading">Struttura operativa banca / FE</x-slot>
|
||
<x-slot name="description">Separiamo la struttura documentale e le gestioni dal tab movimenti, così l’operatore vede subito il mastrino e gli import.</x-slot>
|
||
|
||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-3">
|
||
<div class="rounded-xl border bg-slate-50 p-4">
|
||
<div class="text-xs font-medium uppercase tracking-wide text-slate-500">Struttura archivio banca</div>
|
||
<div class="mt-2 text-sm font-semibold text-slate-900">{{ $archiveSummary['banca_folder'] ?? 'banca_cc/<conto>/<anno>' }}</div>
|
||
<div class="mt-2 text-xs text-slate-600">Qui finiscono estratti, saldi, file importati e documenti conto in modo uniforme su tutti gli stabili.</div>
|
||
<div class="mt-3 rounded-lg border bg-white p-3 text-xs text-slate-700">
|
||
<div><span class="font-semibold">Base stabile:</span> {{ $archiveSummary['base_folder'] ?? '—' }}</div>
|
||
<div class="mt-1"><span class="font-semibold">Anno operativo:</span> {{ $archiveSummary['reference_year'] ?? '—' }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rounded-xl border bg-slate-50 p-4">
|
||
<div class="text-xs font-medium uppercase tracking-wide text-slate-500">Gestioni contabili</div>
|
||
<div class="mt-2 text-sm font-semibold text-slate-900">{{ $archiveSummary['gestione_label'] ?? 'Gestione da associare' }}</div>
|
||
<div class="mt-1 text-xs text-slate-600">Le gestioni storiche restano qui, fuori dal mastrino, così il tab movimenti resta leggibile anche con molte annualità.</div>
|
||
<div class="mt-3 max-h-80 space-y-2 overflow-y-auto pr-1">
|
||
@forelse($gestioniRilevate as $gestione)
|
||
<div class="rounded-lg border bg-white px-3 py-2 text-xs text-slate-700">
|
||
<div class="flex items-center justify-between gap-2">
|
||
<span class="font-semibold">{{ $gestione['folder'] }}</span>
|
||
<span class="rounded-full px-2 py-0.5 {{ $gestione['active'] ? 'bg-emerald-100 text-emerald-700' : 'bg-slate-100 text-slate-600' }}">{{ $gestione['stato'] !== '' ? $gestione['stato'] : 'attiva' }}</span>
|
||
</div>
|
||
<div class="mt-1">{{ $gestione['label'] }}</div>
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed bg-white px-3 py-3 text-xs text-slate-500">Nessuna gestione ordinaria/straordinaria rilevata.</div>
|
||
@endforelse
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rounded-xl border bg-slate-50 p-4">
|
||
<div class="text-xs font-medium uppercase tracking-wide text-slate-500">Struttura FE pronta</div>
|
||
<div class="mt-2 space-y-2 text-xs text-slate-700">
|
||
<div class="rounded-lg border bg-white px-3 py-2">
|
||
<div class="font-semibold text-slate-900">{{ $archiveSummary['fatture_xml_folder'] ?? 'fatture_xml/<anno>' }}</div>
|
||
<div class="mt-1">XML originali per anno, indipendenti dal PDF.</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white px-3 py-2">
|
||
<div class="font-semibold text-slate-900">{{ $archiveSummary['fatture_pdf_folder'] ?? 'fatture/<gestione>' }}</div>
|
||
<div class="mt-1">PDF protocollati per gestione, pronti per ACL atomici su anno e tipo gestione.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4 grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,1.1fr)_minmax(320px,0.9fr)]">
|
||
<div class="rounded-lg border bg-slate-50 p-3">
|
||
<div class="text-sm font-semibold text-slate-900">Criteri di riconciliazione e quadratura</div>
|
||
<div class="mt-1 text-xs text-slate-600">Questi bucket restano visibili come riferimento operativo, ma fuori dal tab movimenti.</div>
|
||
<div class="mt-3 grid grid-cols-1 gap-3 md:grid-cols-2">
|
||
@foreach($ricBuckets as $bucket)
|
||
<div class="rounded-lg border bg-white p-3 text-sm">
|
||
<div class="font-semibold text-slate-900">{{ $bucket['title'] }}</div>
|
||
<div class="mt-1 text-xs text-slate-600">{{ $bucket['detail'] }}</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rounded-lg border bg-slate-50 p-3">
|
||
<div class="text-sm font-semibold text-slate-900">Regole operative di struttura</div>
|
||
<div class="mt-3 space-y-2 text-xs text-slate-700">
|
||
<div class="rounded-lg border bg-white px-3 py-2">I movimenti banca restano sempre sul <span class="font-semibold">conto selezionato</span> e vanno associati a una <span class="font-semibold">gestione</span>.</div>
|
||
<div class="rounded-lg border bg-white px-3 py-2">Per questo stabile trattiamo solo <span class="font-semibold">ordinaria</span> e <span class="font-semibold">straordinaria</span>; il riscaldamento non entra nel flusso.</div>
|
||
<div class="rounded-lg border bg-white px-3 py-2">Le FE acqua possono confluire sulla voce <span class="font-semibold">ACQ</span> quando il PDF e il pagamento banca danno un aggancio coerente.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</x-filament::section>
|
||
@elseif($this->hubTab === 'riconciliazione')
|
||
<x-filament::section>
|
||
<x-slot name="heading">Riconciliazione guidata</x-slot>
|
||
<x-slot name="description">Pre-riconciliazione automatica con candidati. L’operatore verifica un movimento alla volta e usa il mastrino dove il match non è certo.</x-slot>
|
||
|
||
<div class="grid grid-cols-2 gap-3 xl:grid-cols-4">
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Totale movimenti</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">{{ $ricStats['totale'] ?? 0 }}</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Senza prima nota</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">{{ $ricStats['senza_prima_nota'] ?? 0 }}</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Già collegati</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">{{ $ricStats['collegati'] ?? 0 }}</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-slate-500">Da confermare</div>
|
||
<div class="mt-1 text-lg font-semibold text-slate-900">{{ $ricStats['da_confermare'] ?? 0 }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
@if(! $contoImport)
|
||
<div class="mt-4 rounded-lg border border-dashed bg-white p-4 text-sm text-slate-500">Seleziona un conto per avviare la riconciliazione.</div>
|
||
@elseif(! $ricCurrent)
|
||
<div class="mt-4 rounded-lg border border-dashed bg-white p-4 text-sm text-slate-500">Nessun movimento in coda. Il conto sembra già lavorato oppure i movimenti sono tutti collegati.</div>
|
||
@else
|
||
<div class="mt-4 grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,1fr)_minmax(340px,0.9fr)]">
|
||
<div class="rounded-xl border bg-slate-50 p-4">
|
||
<div class="flex items-center justify-between gap-3">
|
||
<div>
|
||
<div class="text-xs uppercase tracking-wide text-slate-500">Movimento in lavorazione</div>
|
||
<div class="mt-1 text-sm text-slate-600">{{ ($this->riconciliazioneOffset ?? 0) + 1 }} di {{ count($ricQueue) }}</div>
|
||
</div>
|
||
<div class="flex gap-2">
|
||
<x-filament::button size="sm" color="gray" wire:click="previousRiconciliazione">Precedente</x-filament::button>
|
||
<x-filament::button size="sm" color="gray" wire:click="nextRiconciliazione">Successivo</x-filament::button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4 rounded-lg border bg-white p-4">
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div>
|
||
<div class="text-sm font-semibold text-slate-900">{{ $ricCurrent['data'] }}</div>
|
||
<div class="mt-1 text-xs text-slate-500">Stato: {{ $ricCurrent['stato'] }}</div>
|
||
</div>
|
||
<div class="text-right text-lg font-semibold {{ $ricCurrent['importo'] >= 0 ? 'text-emerald-700' : 'text-rose-700' }}">
|
||
€ {{ number_format((float) $ricCurrent['importo'], 2, ',', '.') }}
|
||
</div>
|
||
</div>
|
||
<div class="mt-3 text-sm text-slate-700">{{ $ricCurrent['descrizione'] }}</div>
|
||
<div class="mt-4 flex flex-wrap gap-2">
|
||
<x-filament::button size="sm" color="primary" wire:click="goToHubTab('movimenti')">Apri nel mastrino</x-filament::button>
|
||
@if($ricCurrent['has_registrazione'])
|
||
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-medium text-emerald-700">Ha già una prima nota</span>
|
||
@elseif(! empty($ricCurrent['match_label']))
|
||
<span class="rounded-full bg-emerald-100 px-3 py-1 text-xs font-medium text-emerald-700">{{ $ricCurrent['match_label'] }}</span>
|
||
@else
|
||
<span class="rounded-full bg-amber-100 px-3 py-1 text-xs font-medium text-amber-700">Da collegare</span>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rounded-xl border bg-white p-4">
|
||
<div class="text-sm font-semibold text-slate-900">Candidati automatici</div>
|
||
<div class="mt-1 text-xs text-slate-600">
|
||
@if($ricCurrent['importo'] >= 0)
|
||
Match su canoni affitto e incassi registrati per importo, data e testo della causale.
|
||
@else
|
||
Match su fatture fornitore contabili per importo netto, data e denominazione del fornitore.
|
||
@endif
|
||
</div>
|
||
|
||
<div class="mt-4 space-y-3">
|
||
@forelse($ricCandidates as $candidate)
|
||
<div class="rounded-lg border bg-slate-50 p-3">
|
||
<div class="flex items-start justify-between gap-3">
|
||
<div>
|
||
<div class="text-xs uppercase tracking-wide text-slate-500">{{ $candidate['tipo'] }}</div>
|
||
<div class="mt-1 text-sm font-semibold text-slate-900">{{ $candidate['titolo'] }}</div>
|
||
</div>
|
||
<div class="rounded-full bg-primary-100 px-2.5 py-1 text-xs font-semibold text-primary-700">{{ $candidate['score'] }}%</div>
|
||
</div>
|
||
<div class="mt-2 text-sm text-slate-700">{{ $candidate['dettaglio'] }}</div>
|
||
<div class="mt-2 flex flex-wrap gap-4 text-xs text-slate-500">
|
||
<span>Data: {{ $candidate['data'] }}</span>
|
||
<span>Importo: € {{ number_format((float) $candidate['importo'], 2, ',', '.') }}</span>
|
||
</div>
|
||
<div class="mt-2 text-xs text-slate-600">{{ $candidate['motivo'] }}</div>
|
||
@if(($candidate['azione'] ?? null) === 'riconcilia_affitto')
|
||
<div class="mt-3">
|
||
<x-filament::button size="sm" color="success" wire:click="riconciliaCanoneAffitto({{ (int) $candidate['canone_dovuto_id'] }})">
|
||
Chiudi canone affitto
|
||
</x-filament::button>
|
||
</div>
|
||
@elseif(($candidate['azione'] ?? null) === 'riconcilia_pagamento_fornitore')
|
||
<div class="mt-3">
|
||
<x-filament::button size="sm" color="success" wire:click="riconciliaPagamentoFornitore({{ (int) $candidate['fattura_fornitore_id'] }})">
|
||
Registra pagamento fornitore
|
||
</x-filament::button>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed bg-slate-50 p-4 text-sm text-slate-500">Nessun candidato convincente. In questo caso l’operatore lavora dal mastrino e conferma manualmente il collegamento.</div>
|
||
@endforelse
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
</x-filament::section>
|
||
@elseif($this->hubTab === 'movimenti')
|
||
<x-filament::section>
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div>
|
||
<div class="text-sm font-semibold">Import unico estratti</div>
|
||
<div class="mt-1 text-xs text-gray-600">
|
||
Seleziona il conto o la cassa corretta e importa il file nel formato relativo. Il collegamento al conto resta esplicito, senza inferenze dal file.
|
||
</div>
|
||
<div class="mt-2 text-xs text-slate-700">Per i file <span class="font-semibold">.qif</span> puliamo i movimenti nei gruppi operativi e leggiamo <span class="font-semibold">CBILL</span>, <span class="font-semibold">SIA</span>, commissioni e fornitore favorito per proporre subito il collegamento con fatture e prima nota.</div>
|
||
@if($contoImport)
|
||
<div class="mt-2 text-xs text-gray-700">
|
||
Conto selezionato: <span class="font-medium">{{ $contoImport['label'] }}</span>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
<div class="flex flex-wrap gap-2">
|
||
<x-filament::button type="button" color="gray" wire:click="mountAction('aggiungi_movimento_manuale')">
|
||
Nuovo movimento manuale
|
||
</x-filament::button>
|
||
<x-filament::button type="button" color="primary" wire:click="mountAction('importa_estratto_unificato')">
|
||
Importa estratto
|
||
</x-filament::button>
|
||
</div>
|
||
</div>
|
||
|
||
</x-filament::section>
|
||
|
||
<x-filament::section>
|
||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||
<div class="text-sm font-medium">
|
||
{{ $hdr['label'] }}
|
||
@if($hdr['saldo'] !== null && $hdr['data'])
|
||
· saldo al: {{ $hdr['data'] }} € {{ number_format((float) $hdr['saldo'], 2, ',', '.') }}
|
||
@endif
|
||
</div>
|
||
<div class="text-xs text-gray-600">Filtra per periodo/gestione dai filtri tabella.</div>
|
||
</div>
|
||
|
||
@if($this->movimentiFocusFrom || $this->movimentiFocusTo)
|
||
<div class="mt-4 flex flex-wrap items-center justify-between gap-3 rounded-lg border bg-amber-50 p-3 text-sm text-amber-900">
|
||
<div>
|
||
Filtro rapido attivo sul periodo
|
||
<span class="font-semibold">{{ $this->movimentiFocusFrom ?: '—' }}</span>
|
||
→
|
||
<span class="font-semibold">{{ $this->movimentiFocusTo ?: '—' }}</span>
|
||
</div>
|
||
<x-filament::button size="sm" color="gray" wire:click="clearMovimentiFocus">Rimuovi focus</x-filament::button>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="mt-4 grid grid-cols-1 gap-3 lg:grid-cols-4">
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-gray-500">Periodo</div>
|
||
<div class="mt-2 flex flex-wrap gap-2">
|
||
<select wire:model="periodoTipo" class="rounded border-gray-300 text-xs">
|
||
<option value="mese">Mese</option>
|
||
<option value="trimestre">Trimestre</option>
|
||
</select>
|
||
<select wire:model="periodoValore" class="rounded border-gray-300 text-xs">
|
||
@foreach(($periodoOptions[$periodoTipo] ?? []) as $val => $label)
|
||
<option value="{{ $val }}">{{ $label }}</option>
|
||
@endforeach
|
||
</select>
|
||
<input type="number" wire:model="periodoAnno" class="w-24 rounded border-gray-300 text-xs" min="2000" max="2100" />
|
||
</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-gray-500">Totali periodo ({{ $periodo['label'] ?? '' }})</div>
|
||
<div class="mt-2 text-xs">
|
||
<div>Movimenti: <span class="font-semibold">{{ $periodo['count'] ?? 0 }}</span></div>
|
||
<div>Entrate: <span class="font-semibold">€ {{ number_format((float)($periodo['entrate'] ?? 0), 2, ',', '.') }}</span></div>
|
||
<div>Uscite: <span class="font-semibold">€ {{ number_format((float)($periodo['uscite'] ?? 0), 2, ',', '.') }}</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-gray-500">Periodo rate (senza anno)</div>
|
||
<div class="mt-2 flex flex-wrap gap-2">
|
||
<select wire:model="periodoRate" class="rounded border-gray-300 text-xs">
|
||
<option value="">—</option>
|
||
@foreach($periodoRateOptions as $val => $label)
|
||
<option value="{{ $val }}">{{ $label }}</option>
|
||
@endforeach
|
||
</select>
|
||
</div>
|
||
<div class="mt-2 text-xs">
|
||
<div>Movimenti: <span class="font-semibold">{{ $periodoRate['count'] ?? 0 }}</span></div>
|
||
<div>Entrate: <span class="font-semibold">€ {{ number_format((float)($periodoRate['entrate'] ?? 0), 2, ',', '.') }}</span></div>
|
||
<div>Uscite: <span class="font-semibold">€ {{ number_format((float)($periodoRate['uscite'] ?? 0), 2, ',', '.') }}</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="rounded-lg border bg-white p-3">
|
||
<div class="text-xs text-gray-500">Saldo periodo</div>
|
||
<div class="mt-3 text-lg font-semibold">€ {{ number_format((float)($periodo['saldo'] ?? 0), 2, ',', '.') }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4 rounded-lg border bg-slate-50 p-3">
|
||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||
<div>
|
||
<div class="text-sm font-semibold text-slate-900">Snapshot ufficiali / estratti conto</div>
|
||
<div class="text-xs text-slate-600">I saldi ufficiali ancorano il ricalcolo del conto anche quando il conto non ha IBAN.</div>
|
||
</div>
|
||
<x-filament::button type="button" size="sm" color="gray" wire:click="mountAction('registra_saldo_estratto')">
|
||
Registra saldo / estratto
|
||
</x-filament::button>
|
||
</div>
|
||
|
||
<div class="mt-3 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||
@forelse($snapshotCards as $card)
|
||
<div class="rounded-lg border bg-white p-3 text-sm">
|
||
<div class="flex items-start justify-between gap-2">
|
||
<div class="font-medium text-slate-900">{{ $card['tipo'] }}</div>
|
||
<div class="text-xs text-slate-500">{{ $card['data'] ?? '—' }}</div>
|
||
</div>
|
||
@if(!empty($card['is_partenza_contabile']))
|
||
<div class="mt-2 inline-flex items-center gap-1 rounded-full bg-sky-100 px-2.5 py-1 text-[11px] font-semibold text-sky-700">
|
||
<x-heroicon-o-flag class="h-3.5 w-3.5" />
|
||
Partenza contabile
|
||
</div>
|
||
@endif
|
||
<div class="mt-2 text-lg font-semibold text-slate-900">€ {{ number_format((float) ($card['saldo'] ?? 0), 2, ',', '.') }}</div>
|
||
<div class="mt-1 text-xs text-slate-600">{{ $card['periodo'] }}</div>
|
||
@if(!empty($card['documento_url']) && !empty($card['documento_label']))
|
||
<button type="button" wire:click="openSaldoDocumento({{ $card['id'] }})" class="mt-2 inline-flex text-xs font-medium text-indigo-700 hover:underline">{{ $card['documento_label'] }}</button>
|
||
@endif
|
||
</div>
|
||
@empty
|
||
<div class="rounded-lg border border-dashed bg-white p-3 text-sm text-slate-500 md:col-span-2 xl:col-span-3">Nessuno snapshot ufficiale registrato per il conto selezionato.</div>
|
||
@endforelse
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4">
|
||
{{ $this->table }}
|
||
</div>
|
||
</x-filament::section>
|
||
@elseif($this->hubTab === 'associazione_causali')
|
||
<x-filament::section>
|
||
<x-slot name="heading">Associazione Causali e Regole Bancarie</x-slot>
|
||
<x-slot name="description">Configura i conti Dare e Avere per ciascun codice causale rilevato nei movimenti banca.</x-slot>
|
||
|
||
@php($assocRows = $this->getCausaliAssociazioneRows())
|
||
|
||
<div class="mt-4 overflow-x-auto rounded-lg border">
|
||
<table class="w-full text-left text-sm divide-y divide-gray-200 dark:divide-gray-800">
|
||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||
<tr>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300">Causale bancaria</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300">Descrizione rilevata</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300 text-center">Movimenti</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300">Conto Dare (Entrate)</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300">Conto Avere (Uscite)</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300 text-center">Stato regola</th>
|
||
<th class="px-4 py-3 font-semibold text-gray-700 dark:text-gray-300 text-right">Azioni</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-gray-200 dark:divide-gray-800 bg-white dark:bg-gray-900">
|
||
@forelse($assocRows as $row)
|
||
<tr>
|
||
<td class="px-4 py-3 font-mono font-bold text-gray-900 dark:text-gray-100">
|
||
{{ $row['causale'] }}
|
||
</td>
|
||
<td class="px-4 py-3 text-gray-600 dark:text-gray-400">
|
||
{{ $row['descrizione'] }}
|
||
</td>
|
||
<td class="px-4 py-3 text-center font-semibold">
|
||
{{ $row['count'] }}
|
||
</td>
|
||
<td class="px-4 py-3 text-gray-900 dark:text-gray-100">
|
||
@if($row['conto_dare'])
|
||
<span class="inline-flex items-center rounded-md bg-blue-50 dark:bg-blue-900/30 px-2 py-1 text-xs font-medium text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-blue-800">
|
||
{{ $row['conto_dare'] }}
|
||
</span>
|
||
@else
|
||
<span class="text-xs text-gray-400">—</span>
|
||
@endif
|
||
</td>
|
||
<td class="px-4 py-3 text-gray-900 dark:text-gray-100">
|
||
@if($row['conto_avere'])
|
||
<span class="inline-flex items-center rounded-md bg-blue-50 dark:bg-blue-900/30 px-2 py-1 text-xs font-medium text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-blue-800">
|
||
{{ $row['conto_avere'] }}
|
||
</span>
|
||
@else
|
||
<span class="text-xs text-gray-400">—</span>
|
||
@endif
|
||
</td>
|
||
<td class="px-4 py-3 text-center">
|
||
@if($row['attiva'])
|
||
<span class="inline-flex items-center gap-1 rounded-full bg-emerald-100 dark:bg-emerald-950/30 px-2.5 py-0.5 text-xs font-semibold text-emerald-800 dark:text-emerald-300 border border-emerald-200 dark:border-emerald-900">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
|
||
Attiva
|
||
</span>
|
||
@else
|
||
<span class="inline-flex items-center gap-1 rounded-full bg-amber-100 dark:bg-amber-950/30 px-2.5 py-0.5 text-xs font-semibold text-amber-800 dark:text-amber-300 border border-amber-200 dark:border-amber-900">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-amber-500"></span>
|
||
Configurazione mancante
|
||
</span>
|
||
@endif
|
||
</td>
|
||
<td class="px-4 py-3 text-right">
|
||
<x-filament::button
|
||
size="sm"
|
||
color="{{ $row['attiva'] ? 'gray' : 'primary' }}"
|
||
wire:click="startConfiguringRegolaCausale('{{ $row['causale'] }}')"
|
||
>
|
||
{{ $row['attiva'] ? 'Modifica regola' : 'Configura regola' }}
|
||
</x-filament::button>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="7" class="px-4 py-6 text-center text-gray-500">
|
||
Nessun movimento banca importato con codici causale rilevati.
|
||
</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</x-filament::section>
|
||
@endif
|
||
@endif
|
||
</div>
|
||
|
||
@if($this->pdfViewerUrl)
|
||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-slate-950/70 p-4">
|
||
<div class="flex h-[92vh] w-full max-w-7xl flex-col overflow-hidden rounded-2xl bg-white shadow-2xl">
|
||
<div class="flex items-center justify-between border-b px-5 py-3">
|
||
<div>
|
||
<div class="text-sm font-semibold text-slate-900">{{ $this->pdfViewerTitle ?? 'Documento PDF' }}</div>
|
||
<div class="text-xs text-slate-500">Viewer PDF inline standard per saldi, estratti e documenti collegati.</div>
|
||
</div>
|
||
<div class="flex items-center gap-2">
|
||
@if($this->pdfViewerOpenUrl)
|
||
<a href="{{ $this->pdfViewerOpenUrl }}" target="_blank" rel="noopener" class="inline-flex items-center rounded-md bg-slate-800 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-700">Apri in nuova scheda</a>
|
||
@endif
|
||
<x-filament::button type="button" color="gray" size="sm" wire:click="closePdfViewer">Chiudi</x-filament::button>
|
||
</div>
|
||
</div>
|
||
<div class="min-h-0 flex-1 bg-slate-100 p-4">
|
||
@include('filament.modals.pdf-viewer', ['url' => $this->pdfViewerUrl, 'openUrl' => $this->pdfViewerOpenUrl, 'title' => $this->pdfViewerTitle])
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<x-filament-actions::modals />
|
||
</x-filament-panels::page>
|