netgescon-day0/app/Filament/Pages/Contabilita/SituazioneIniziale.php

2810 lines
106 KiB
PHP

<?php
namespace App\Filament\Pages\Contabilita;
use App\Models\DatiBancari;
use App\Models\DocumentoStabile;
use App\Models\FatturaElettronica;
use App\Models\GestioneContabile;
use App\Models\PersonaUnitaRelazione;
use App\Models\RegistroRitenuteAcconto;
use App\Models\Stabile;
use App\Models\UnitaImmobiliare;
use App\Models\UnitaImmobiliareNominativo;
use App\Models\User;
use App\Modules\Contabilita\Models\MovimentoBanca;
use App\Modules\Contabilita\Models\SaldoConto;
use App\Support\StabileContext;
use Filament\Pages\Page;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use Livewire\Attributes\Url;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
use Livewire\WithFileUploads;
use UnitEnum;
use BackedEnum;
class SituazioneIniziale extends Page
{
use WithFileUploads;
protected static ?string $navigationLabel = 'Situazione iniziale';
protected static ?string $title = 'Situazione iniziale';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-scale';
protected static UnitEnum|string|null $navigationGroup = 'Contabilità';
protected static ?int $navigationSort = 12;
protected static ?string $slug = 'contabilita/situazione-iniziale';
protected string $view = 'filament.pages.contabilita.situazione-iniziale';
#[Url(as: 'legacy_year')]
public ?string $legacyYear = null;
#[Url(as: 'data')]
public ?string $dataBilancio = null;
#[Url(as: 'saldo_manuale')]
public ?string $saldoBancaManuale = null;
#[Url(as: 'tab')]
public string $tab = 'bilancio';
#[Url(as: 'dett')]
public ?string $dett = null;
public array $legacyYears = [];
/** @var array<string, string> Map cartella legacy (0001) -> label utente (es. 2024-25) */
public array $legacyYearLabels = [];
public array $conti = [];
public array $risorseFinanziarie = [];
public array $bilancio = [];
public array $conguagliExcel = [];
public array $conguagliExcelCols = [];
// CRUD stile "Excel" (dati legacy su gescon_import)
public array $creDebRows = [];
/** @var array<int, array<string, mixed>> */
public array $creDebEdit = [];
public array $formCreDebAdd = [
'cod_voc' => null,
'des_voce' => null,
'descrizione' => null,
'importo_euro' => null,
'n_stra' => null,
'incluso' => 1,
];
public array $dettTabRows = [];
/** @var array<int, array<string, mixed>> */
public array $dettTabEdit = [];
public array $formDettTabAdd = [
'cod_tab' => 'CONG.O',
'id_cond' => null,
'cond_inquil' => null,
'mm' => null,
'cons_euro' => null,
'n_stra' => null,
'unico' => 0,
];
public array $formVoce = [
'gestione' => 'ordinaria',
'categoria' => 'crediti',
'n_stra' => null,
'codice' => null,
'descrizione' => null,
'importo_euro' => null,
];
public array $formConguaglio = [
'cod_tab' => 'CONG.O',
'n_stra' => null,
'id_cond' => null,
'cons_euro' => null,
];
public array $detailMeta = [];
public array $detailRows = [];
public ?string $saldoContoId = null;
public ?string $saldoSnapshotData = null;
public ?string $saldoSnapshotPeriodoDa = null;
public ?string $saldoSnapshotPeriodoA = null;
public ?string $saldoSnapshotSaldo = null;
public string $saldoSnapshotTipo = 'estratto_conto';
public ?string $saldoSnapshotNote = null;
public TemporaryUploadedFile|string|null $saldoSnapshotPdf = null;
/** @var array<int, array<string, mixed>> */
public array $saldoSnapshots = [];
/** @var array<string, string> */
public array $vocSpeOptions = [];
// Editing "a riga" (riduce la pagina: si modifica una riga alla volta)
public ?int $editingCreDebId = null;
public ?int $editingDettTabId = null;
// Selezione riga (per mostrare dettaglio voce spesa sotto Crediti/Debiti)
public ?int $selectedCreDebId = null;
// Sub-tab documenti (sotto Crediti/Debiti): voce / FE / RA
public string $detailTab = 'voce';
/** @var array<int, array<string, mixed>> */
public array $feRows = [];
/** @var array<int, int> */
public array $linkedFeIds = [];
/** @var array<int, array<string, mixed>> */
public array $raRows = [];
/** @var array<int, int> */
public array $linkedRaIds = [];
public static function canAccess(): bool
{
$user = Auth::user();
if (! $user instanceof User) {
return false;
}
if ($user->hasAnyRole(['super-admin', 'admin'])) {
return true;
}
return $user->can('contabilita.prima-nota');
}
public function mount(): void
{
$this->legacyYear = is_string($this->legacyYear) ? trim((string) $this->legacyYear) : null;
$this->legacyYear = $this->legacyYear !== '' ? $this->legacyYear : null;
$this->dataBilancio = is_string($this->dataBilancio) ? trim((string) $this->dataBilancio) : null;
$this->dataBilancio = $this->dataBilancio !== '' ? $this->dataBilancio : null;
$this->saldoBancaManuale = is_string($this->saldoBancaManuale) ? trim((string) $this->saldoBancaManuale) : null;
$this->saldoBancaManuale = $this->saldoBancaManuale !== '' ? $this->saldoBancaManuale : null;
$this->tab = is_string($this->tab) ? trim((string) $this->tab) : 'bilancio';
$this->tab = in_array($this->tab, ['bilancio', 'crediti', 'debiti', 'conguagli', 'dettaglio'], true) ? $this->tab : 'bilancio';
$this->dett = is_string($this->dett) ? trim((string) $this->dett) : null;
$this->dett = $this->dett !== '' ? $this->dett : null;
$this->reloadData();
}
public function updatedTab(): void
{
$this->tab = is_string($this->tab) ? trim((string) $this->tab) : 'bilancio';
$this->tab = in_array($this->tab, ['bilancio', 'crediti', 'debiti', 'conguagli', 'dettaglio'], true) ? $this->tab : 'bilancio';
if ($this->tab !== 'dettaglio') {
$this->dett = null;
}
// Reset stati per evitare UI "incastrata" cambiando tab
$this->editingCreDebId = null;
$this->editingDettTabId = null;
$this->selectedCreDebId = null;
$this->detailTab = 'voce';
$this->reloadData();
}
public function editCreDebRow(int $id): void
{
if ($id <= 0) {
return;
}
$this->editingCreDebId = $id;
$this->selectedCreDebId = $id;
}
public function cancelEditCreDebRow(): void
{
$this->editingCreDebId = null;
}
public function selectCreDebRow(int $id): void
{
if ($id <= 0) {
$this->selectedCreDebId = null;
return;
}
$this->selectedCreDebId = $id;
// Aggiorna subito i collegamenti documentali per la riga selezionata
$this->reloadDocumenti();
}
public function setDetailTab(string $tab): void
{
$tab = strtolower(trim($tab));
$this->detailTab = in_array($tab, ['voce', 'fe', 'ra'], true) ? $tab : 'voce';
}
public function editDettTabRow(int $id): void
{
if ($id <= 0) {
return;
}
$this->editingDettTabId = $id;
}
public function cancelEditDettTabRow(): void
{
$this->editingDettTabId = null;
}
private function loadCondominiLabels(string $codStabileLegacy, ?string $legacyYear): array
{
$out = $this->loadLocalCondominiLabels();
// Tabella "condomini" (più snella) se presente
if (Schema::connection('gescon_import')->hasTable('condomini')) {
$q = DB::connection('gescon_import')->table('condomini')
->where('cod_stabile', $codStabileLegacy);
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('condomini', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$rows = $q->get(['id_cond', 'cognome', 'nome', 'cod_cond']);
foreach ($rows as $r) {
$id = trim((string) ($r->id_cond ?? ''));
if ($id === '') {
continue;
}
$label = trim(trim((string) ($r->cognome ?? '')) . ' ' . trim((string) ($r->nome ?? '')));
if ($label === '') {
$label = trim((string) ($r->cod_cond ?? ''));
}
if (! isset($out[$id]) || trim((string) $out[$id]) === '') {
$out[$id] = $label !== '' ? $label : $id;
}
}
}
// Fallback su "condomin" (più completa)
if (Schema::connection('gescon_import')->hasTable('condomin')) {
$q = DB::connection('gescon_import')->table('condomin')
->where('cod_stabile', $codStabileLegacy);
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('condomin', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$rows = $q->get(['id_cond', 'denominazione', 'nom_cond', 'cognome', 'nome', 'cod_cond']);
foreach ($rows as $r) {
$id = trim((string) ($r->id_cond ?? ''));
if ($id === '') {
continue;
}
$label = trim((string) ($r->denominazione ?? ''));
if ($label === '') {
$label = trim((string) ($r->nom_cond ?? ''));
}
if ($label === '') {
$label = trim(trim((string) ($r->cognome ?? '')) . ' ' . trim((string) ($r->nome ?? '')));
}
if ($label === '') {
$label = trim((string) ($r->cod_cond ?? ''));
}
if (! isset($out[$id]) || trim((string) $out[$id]) === '') {
$out[$id] = $label !== '' ? $label : $id;
}
}
}
return $out;
}
/**
* @return array<string, string>
*/
private function loadLocalCondominiLabels(): array
{
$stabileId = $this->getActiveStabile()?->id;
if (! $stabileId || ! Schema::hasTable('unita_immobiliari') || ! Schema::hasColumn('unita_immobiliari', 'legacy_cond_id')) {
return [];
}
$referenceDate = $this->resolveReferenceDate();
$units = UnitaImmobiliare::query()
->where('stabile_id', (int) $stabileId)
->whereNotNull('legacy_cond_id')
->get(['id', 'legacy_cond_id', 'scala', 'interno', 'denominazione']);
if ($units->isEmpty()) {
return [];
}
$unitIds = $units->pluck('id')->map(fn ($id) => (int) $id)->all();
$namesByUnit = [];
if (Schema::hasTable('persone_unita_relazioni')) {
$relations = PersonaUnitaRelazione::query()
->with('persona')
->whereIn('unita_id', $unitIds)
->where('attivo', true)
->where(function ($query) use ($referenceDate): void {
$query->whereNull('data_inizio')->orWhere('data_inizio', '<=', $referenceDate->toDateString());
})
->where(function ($query) use ($referenceDate): void {
$query->whereNull('data_fine')->orWhere('data_fine', '>=', $referenceDate->toDateString());
})
->get();
foreach ($relations as $relation) {
$unitId = (int) $relation->unita_id;
$role = strtoupper((string) ($relation->ruolo_rate ?: PersonaUnitaRelazione::deriveRuoloRate($relation->tipo_relazione)));
if (! in_array($role, ['C', 'I'], true)) {
continue;
}
$name = trim((string) ($relation->persona?->nome_display ?? $relation->persona?->nome_completo ?? ''));
if ($name === '') {
continue;
}
$namesByUnit[$unitId][$role][$name] = $name;
}
}
if (Schema::hasTable('unita_immobiliare_nominativi')) {
$legacyNames = UnitaImmobiliareNominativo::query()
->where('stabile_id', (int) $stabileId)
->whereIn('unita_immobiliare_id', $unitIds)
->where(function ($query) use ($referenceDate): void {
$query->whereNull('data_inizio')->orWhere('data_inizio', '<=', $referenceDate->toDateString());
})
->where(function ($query) use ($referenceDate): void {
$query->whereNull('data_fine')->orWhere('data_fine', '>=', $referenceDate->toDateString());
})
->get(['unita_immobiliare_id', 'ruolo', 'nominativo']);
foreach ($legacyNames as $row) {
$unitId = (int) ($row->unita_immobiliare_id ?? 0);
$role = strtoupper((string) ($row->ruolo ?? ''));
if (! in_array($role, ['C', 'I'], true)) {
continue;
}
$name = trim((string) ($row->nominativo ?? ''));
if ($name === '') {
continue;
}
$namesByUnit[$unitId][$role][$name] = $name;
}
}
$labels = [];
foreach ($units as $unit) {
$legacyCondId = trim((string) ($unit->legacy_cond_id ?? ''));
if ($legacyCondId === '') {
continue;
}
$pieces = [];
$scala = trim((string) ($unit->scala ?? ''));
$interno = trim((string) ($unit->interno ?? ''));
$denominazione = trim((string) ($unit->denominazione ?? ''));
if ($scala !== '' || $interno !== '') {
$unitLabel = trim(($scala !== '' ? ('Scala ' . $scala) : '') . ($interno !== '' ? (' Int. ' . $interno) : ''));
if ($unitLabel !== '') {
$pieces[] = $unitLabel;
}
} elseif ($denominazione !== '') {
$pieces[] = $denominazione;
}
$owners = array_values($namesByUnit[(int) $unit->id]['C'] ?? []);
$tenants = array_values($namesByUnit[(int) $unit->id]['I'] ?? []);
if ($owners !== []) {
$pieces[] = 'Prop: ' . implode(', ', $owners);
}
if ($tenants !== []) {
$pieces[] = 'Inq: ' . implode(', ', $tenants);
}
if ($pieces === [] && $denominazione !== '') {
$pieces[] = $denominazione;
}
$labels[$legacyCondId] = $pieces !== [] ? implode(' · ', $pieces) : $legacyCondId;
}
return $labels;
}
private function resolveReferenceDate(): Carbon
{
try {
return $this->dataBilancio ? Carbon::parse($this->dataBilancio)->startOfDay() : now()->startOfDay();
} catch (\Throwable) {
return now()->startOfDay();
}
}
public function updatedLegacyYear(): void
{
$this->legacyYear = is_string($this->legacyYear) ? trim((string) $this->legacyYear) : null;
$this->legacyYear = $this->legacyYear !== '' ? $this->legacyYear : null;
$this->reloadData();
}
public function updatedDataBilancio(): void
{
$this->dataBilancio = is_string($this->dataBilancio) ? trim((string) $this->dataBilancio) : null;
$this->dataBilancio = $this->dataBilancio !== '' ? $this->dataBilancio : null;
$this->reloadData();
}
public function updatedSaldoBancaManuale(): void
{
$this->saldoBancaManuale = is_string($this->saldoBancaManuale) ? trim((string) $this->saldoBancaManuale) : null;
$this->saldoBancaManuale = $this->saldoBancaManuale !== '' ? $this->saldoBancaManuale : null;
$this->reloadData();
}
public function updatedSaldoContoId(): void
{
$this->saldoContoId = is_string($this->saldoContoId) ? trim((string) $this->saldoContoId) : null;
$this->saldoContoId = $this->saldoContoId !== '' ? $this->saldoContoId : null;
$this->loadSaldoSnapshots();
}
public function updatedDett(): void
{
$this->dett = is_string($this->dett) ? trim((string) $this->dett) : null;
$this->dett = $this->dett !== '' ? $this->dett : null;
$this->reloadData();
}
private function getActiveStabile(): ?Stabile
{
$user = Auth::user();
if (! $user instanceof User) {
return null;
}
return StabileContext::getActiveStabile($user);
}
private function reloadData(): void
{
$stabile = $this->getActiveStabile();
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
$this->legacyYears = $this->loadLegacyYears($codStabileLegacy);
$this->legacyYearLabels = $codStabileLegacy ? $this->loadLegacyYearLabels($codStabileLegacy) : [];
// Default/fallback legacyYear:
// - Se l'utente NON ha passato legacy_year in query, default al primo esercizio (tipicamente 0001).
// - Se legacy_year è passato (anche vuoto => "Tutti"), rispettalo.
// - Se legacy_year è valorizzato ma non esiste più tra quelli disponibili, fai fallback al primo disponibile.
$hasLegacyYearParam = request()->query->has('legacy_year');
if (! $hasLegacyYearParam) {
if (! empty($this->legacyYears) && ($this->legacyYear === null || $this->legacyYear === '')) {
$this->legacyYear = $this->legacyYears[0];
}
}
if ($this->legacyYear !== null && $this->legacyYear !== '' && ! in_array($this->legacyYear, $this->legacyYears, true)) {
$this->legacyYear = ! empty($this->legacyYears) ? $this->legacyYears[0] : null;
}
$stabileId = $stabile?->id ? (int) $stabile->id : null;
$this->conti = $stabileId ? $this->loadContiOptions($stabileId) : [];
if (($this->saldoContoId === null || $this->saldoContoId === '') && $this->conti !== []) {
$this->saldoContoId = (string) array_key_first($this->conti);
}
if ($this->saldoContoId !== null && $this->saldoContoId !== '' && ! array_key_exists($this->saldoContoId, $this->conti)) {
$this->saldoContoId = $this->conti !== [] ? (string) array_key_first($this->conti) : null;
}
if (! $this->dataBilancio) {
$this->dataBilancio = $this->defaultDataBilancio();
}
if (! $codStabileLegacy || ! $stabileId) {
$this->bilancio = $this->emptyBilancio();
$this->risorseFinanziarie = [];
$this->conguagliExcel = [];
$this->conguagliExcelCols = [];
$this->detailMeta = [];
$this->detailRows = [];
$this->saldoSnapshots = [];
return;
}
$this->risorseFinanziarie = $this->computeRisorseFinanziarie($stabileId, $this->dataBilancio);
$this->bilancio = $this->computeBilancio($stabileId, $codStabileLegacy, $this->legacyYear, $this->dataBilancio, $this->saldoBancaManuale, $this->risorseFinanziarie);
$this->vocSpeOptions = $this->loadVocSpeOptions($codStabileLegacy, $this->legacyYear);
if ($this->tab === 'conguagli') {
$this->conguagliExcel = [];
$this->conguagliExcelCols = [];
[$this->dettTabRows, $this->dettTabEdit] = $this->loadDettTabCrudRows($codStabileLegacy, $this->legacyYear);
} else {
$this->conguagliExcel = [];
$this->conguagliExcelCols = [];
$this->dettTabRows = [];
$this->dettTabEdit = [];
}
if (in_array($this->tab, ['crediti', 'debiti'], true)) {
$cd = $this->tab === 'crediti' ? 'C' : 'D';
[$this->creDebRows, $this->creDebEdit] = $this->loadCreDebPrecedCrudRows($codStabileLegacy, $this->legacyYear, $cd);
// Liste documentali per sub-tab (FE/RA)
$this->reloadDocumenti();
} else {
$this->creDebRows = [];
$this->creDebEdit = [];
$this->feRows = [];
$this->linkedFeIds = [];
$this->raRows = [];
$this->linkedRaIds = [];
}
$this->reloadDettaglio($codStabileLegacy, $this->legacyYear);
$this->loadSaldoSnapshots();
}
public function saveSaldoSnapshot(): void
{
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
if (! $stabileId) {
$this->addError('saldoContoId', 'Seleziona prima uno stabile attivo.');
return;
}
$validated = $this->validate([
'saldoContoId' => ['required', 'string'],
'saldoSnapshotData' => ['required', 'date'],
'saldoSnapshotPeriodoDa' => ['nullable', 'date'],
'saldoSnapshotPeriodoA' => ['nullable', 'date'],
'saldoSnapshotSaldo' => ['required', 'string'],
'saldoSnapshotTipo' => ['required', 'string', 'in:estratto_conto,saldo_banca,riconciliazione,altro'],
'saldoSnapshotNote' => ['nullable', 'string', 'max:255'],
'saldoSnapshotPdf' => ['nullable', 'file', 'mimes:pdf', 'max:20480'],
]);
$contoId = (int) ($validated['saldoContoId'] ?? 0);
$conto = DatiBancari::query()
->where('stabile_id', $stabileId)
->where('id', $contoId)
->first(['id', 'denominazione_banca', 'iban', 'numero_conto', 'legacy_cod_cassa']);
if (! $conto) {
$this->addError('saldoContoId', 'Conto non valido per lo stabile attivo.');
return;
}
$saldo = $this->parseMoney((string) ($validated['saldoSnapshotSaldo'] ?? ''));
if ($saldo === null) {
$this->addError('saldoSnapshotSaldo', 'Saldo non valido.');
return;
}
$user = Auth::user();
$documento = $this->storeSaldoSnapshotDocumento($stabileId, $conto, $this->saldoSnapshotPdf, $validated);
SaldoConto::query()->updateOrCreate(
[
'stabile_id' => $stabileId,
'conto_id' => $contoId,
'data_saldo' => $validated['saldoSnapshotData'],
],
[
'iban' => $this->normalizeIbanValue($conto->iban),
'periodo_da' => $validated['saldoSnapshotPeriodoDa'] ?? null,
'periodo_a' => $validated['saldoSnapshotPeriodoA'] ?? null,
'saldo' => $saldo,
'tipo_estratto' => $validated['saldoSnapshotTipo'],
'note' => isset($validated['saldoSnapshotNote']) ? trim((string) $validated['saldoSnapshotNote']) : null,
'documento_stabile_id' => $documento?->id,
'created_by' => (int) ($user?->id ?? 0) ?: null,
'updated_by' => (int) ($user?->id ?? 0) ?: null,
]
);
$this->resetSaldoSnapshotForm(false);
$this->reloadData();
}
public function deleteSaldoSnapshot(int $snapshotId): void
{
if ($snapshotId <= 0) {
return;
}
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
if (! $stabileId) {
return;
}
$snapshot = SaldoConto::query()
->where('stabile_id', $stabileId)
->where('id', $snapshotId)
->first();
if (! $snapshot) {
return;
}
$documentoId = (int) ($snapshot->documento_stabile_id ?? 0);
$snapshot->delete();
if ($documentoId > 0) {
DocumentoStabile::query()->where('id', $documentoId)->delete();
}
$this->reloadData();
}
private function loadSaldoSnapshots(): void
{
$this->saldoSnapshots = [];
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
$contoId = is_numeric($this->saldoContoId) ? (int) $this->saldoContoId : 0;
if (! $stabileId || $contoId <= 0 || ! DB::getSchemaBuilder()->hasTable('contabilita_saldi_conti')) {
return;
}
$this->saldoSnapshots = SaldoConto::query()
->with('documento')
->where('stabile_id', $stabileId)
->where('conto_id', $contoId)
->orderByDesc('data_saldo')
->orderByDesc('id')
->get()
->map(function (SaldoConto $row): array {
return [
'id' => (int) $row->id,
'data_saldo' => $row->data_saldo?->format('d/m/Y'),
'periodo' => $this->formatPeriodoEstrattoLabel($row->periodo_da, $row->periodo_a),
'tipo' => $this->formatTipoEstrattoLabel($row->tipo_estratto),
'saldo' => (float) $row->saldo,
'note' => (string) ($row->note ?? ''),
'documento_nome' => $row->documento?->nome_originale,
'documento_url' => $row->documento?->url_view,
];
})
->all();
}
private function resetSaldoSnapshotForm(bool $resetConto = true): void
{
if ($resetConto) {
$this->saldoContoId = $this->conti !== [] ? (string) array_key_first($this->conti) : null;
}
$this->saldoSnapshotData = $this->dataBilancio;
$this->saldoSnapshotPeriodoDa = null;
$this->saldoSnapshotPeriodoA = null;
$this->saldoSnapshotSaldo = null;
$this->saldoSnapshotTipo = 'estratto_conto';
$this->saldoSnapshotNote = null;
$this->saldoSnapshotPdf = null;
$this->resetValidation([
'saldoContoId',
'saldoSnapshotData',
'saldoSnapshotPeriodoDa',
'saldoSnapshotPeriodoA',
'saldoSnapshotSaldo',
'saldoSnapshotTipo',
'saldoSnapshotNote',
'saldoSnapshotPdf',
]);
}
private function formatTipoEstrattoLabel(?string $value): string
{
return match (trim((string) $value)) {
'estratto_conto' => 'Estratto conto',
'saldo_banca' => 'Saldo banca',
'riconciliazione' => 'Riconciliazione',
'altro' => 'Altro',
default => 'Saldo registrato',
};
}
private function formatPeriodoEstrattoLabel(mixed $from, mixed $to): string
{
$fromDate = $from instanceof Carbon ? $from : ($from ? Carbon::parse((string) $from) : null);
$toDate = $to instanceof Carbon ? $to : ($to ? Carbon::parse((string) $to) : null);
if ($fromDate && $toDate) {
return $fromDate->format('d/m/Y') . ' - ' . $toDate->format('d/m/Y');
}
if ($toDate) {
return 'Fino al ' . $toDate->format('d/m/Y');
}
if ($fromDate) {
return 'Dal ' . $fromDate->format('d/m/Y');
}
return 'Periodo non indicato';
}
private function normalizeIbanValue(mixed $value): ?string
{
$iban = is_string($value) ? trim((string) $value) : '';
return $iban !== '' ? $iban : null;
}
private function storeSaldoSnapshotDocumento(int $stabileId, DatiBancari $conto, TemporaryUploadedFile|string|null $file, array $validated): ?DocumentoStabile
{
if (! $file instanceof TemporaryUploadedFile) {
return null;
}
$originalName = trim((string) $file->getClientOriginalName());
$extension = strtolower((string) $file->getClientOriginalExtension());
$extension = $extension !== '' ? $extension : 'pdf';
$storedName = 'estratto-conto-' . $stabileId . '-' . (int) $conto->id . '-' . now()->format('YmdHis') . '.' . $extension;
$storedPath = $file->storeAs('documenti/stabili/' . $stabileId . '/bancari', $storedName, 'public');
return DocumentoStabile::query()->create([
'stabile_id' => $stabileId,
'nome_file' => $storedName,
'nome_originale' => $originalName !== '' ? $originalName : $storedName,
'percorso_file' => $storedPath,
'categoria' => 'bancari',
'tipo_mime' => Storage::disk('public')->mimeType($storedPath),
'dimensione' => Storage::disk('public')->size($storedPath),
'descrizione' => 'Saldo/estratto ' . ($this->conti[(string) $conto->id] ?? ('Conto #' . $conto->id)) . ' · ' . $this->formatPeriodoEstrattoLabel($validated['saldoSnapshotPeriodoDa'] ?? null, $validated['saldoSnapshotPeriodoA'] ?? null),
'caricato_da' => Auth::id(),
]);
}
/**
* Carica etichette user-friendly per le gestioni legacy (tabella "anni" in generale_stabile.mdb).
* Ritorna una mappa: cartella (0001) => label (2024-25 o 2024).
*/
private function loadLegacyYearLabels(string $codStabileLegacy): array
{
if (! Schema::connection('gescon_import')->hasTable('gestioni_annuali')) {
return [];
}
$q = DB::connection('gescon_import')->table('gestioni_annuali')
->where('cod_stabile', $codStabileLegacy);
$rows = $q->get(['cartella', 'anno_ordinario', 'anno_riscaldamento']);
$out = [];
foreach ($rows as $r) {
$cartella = trim((string) ($r->cartella ?? ''));
if ($cartella === '') {
continue;
}
$ord = trim((string) ($r->anno_ordinario ?? ''));
$risc = trim((string) ($r->anno_riscaldamento ?? ''));
$parts = [];
if ($ord !== '') {
$parts[] = 'Ord. ' . $ord;
}
if ($risc !== '' && $risc !== $ord) {
$parts[] = 'Risc. ' . $risc;
}
$parts[] = 'Dir. ' . $cartella;
$out[$cartella] = implode(' · ', $parts);
}
return $out;
}
private function reloadDocumenti(): void
{
$this->feRows = [];
$this->linkedFeIds = [];
$this->raRows = [];
$this->linkedRaIds = [];
// Documenti FE/RA: normalmente solo sui DEBITI (fatture passive + ritenute da versare)
if ($this->tab !== 'debiti') {
return;
}
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $stabileId) {
return;
}
$cd = 'D';
$this->feRows = $this->loadFeRows($stabileId, $cd);
$this->raRows = $this->loadRaRows($stabileId);
if (! $this->selectedCreDebId || ! $codStabileLegacy || ! $this->legacyYear) {
return;
}
$this->linkedFeIds = $this->loadLinkedFeIds($codStabileLegacy, $this->legacyYear, (int) $this->selectedCreDebId, $cd);
$this->linkedRaIds = $this->loadLinkedRaIds($codStabileLegacy, $this->legacyYear, (int) $this->selectedCreDebId, $cd);
}
/**
* @return array<int, array<string, mixed>>
*/
private function loadFeRows(int $stabileId, string $cd): array
{
$cd = strtoupper(trim($cd));
$q = FatturaElettronica::query()->where('stabile_id', $stabileId);
// Per i debiti ha senso mostrare prima le fatture da pagare.
if ($cd === 'D') {
$q->where('stato', '!=', 'pagata');
}
// Situazione iniziale: mostriamo solo documenti antecedenti alla data bilancio (cut-off).
// Esempio: bilancio al 01/01/2024 => fatture 2023.
if (is_string($this->dataBilancio) && $this->dataBilancio !== '') {
try {
$cutoff = Carbon::parse($this->dataBilancio)->startOfDay();
$q->whereDate('data_fattura', '<', $cutoff->toDateString());
} catch (\Throwable) {
// ignore
}
}
$rows = $q
->orderBy('data_fattura', 'asc')
->orderBy('id', 'asc')
->limit(100)
->get();
return $rows->map(function (FatturaElettronica $f) {
return [
'id' => (int) $f->id,
'data_fattura' => $f->data_fattura?->format('Y-m-d'),
'data_scadenza' => $f->data_scadenza?->format('Y-m-d'),
'numero_fattura' => (string) ($f->numero_fattura ?? ''),
'fornitore_denominazione' => (string) ($f->fornitore_denominazione ?? ''),
'totale' => is_numeric($f->totale) ? (float) $f->totale : null,
'stato' => (string) ($f->stato ?? ''),
];
})->all();
}
/**
* @return array<int, array<string, mixed>>
*/
private function loadRaRows(int $stabileId): array
{
$rows = RegistroRitenuteAcconto::query()
->with(['fornitore', 'gestione'])
->whereHas('gestione', function ($q) use ($stabileId) {
$q->where('stabile_id', $stabileId);
})
->whereIn('stato_versamento', ['da_versare', 'compensata'])
->orderBy('data_competenza', 'desc')
->orderBy('id', 'desc')
->limit(100)
->get();
return $rows->map(function (RegistroRitenuteAcconto $r) {
$fornitoreLabel = '';
try {
$fornitoreLabel = (string) ($r->fornitore?->ragione_sociale ?? '');
} catch (\Throwable) {
$fornitoreLabel = '';
}
return [
'id' => (int) $r->id,
'data_competenza' => $r->data_competenza?->format('Y-m-d'),
'numero_progressivo' => (int) ($r->numero_progressivo ?? 0),
'fornitore' => $fornitoreLabel,
'importo_ritenuta' => is_numeric($r->importo_ritenuta) ? (float) $r->importo_ritenuta : null,
'stato_versamento' => (string) ($r->stato_versamento ?? ''),
];
})->all();
}
/**
* @return array<int, int>
*/
private function loadLinkedFeIds(string $codStabileLegacy, string $legacyYear, int $creDebPrecedId, string $cd): array
{
if (! Schema::hasTable('contabilita_situazione_iniziale_fe_links')) {
return [];
}
return DB::table('contabilita_situazione_iniziale_fe_links')
->where('cod_stabile_legacy', $codStabileLegacy)
->where('legacy_year', $legacyYear)
->where('cre_deb_preced_id', $creDebPrecedId)
->where('c_d', strtoupper(trim($cd)))
->pluck('fattura_elettronica_id')
->map(fn ($v) => (int) $v)
->values()
->all();
}
/**
* @return array<int, int>
*/
private function loadLinkedRaIds(string $codStabileLegacy, string $legacyYear, int $creDebPrecedId, string $cd): array
{
if (! Schema::hasTable('contabilita_situazione_iniziale_ra_links')) {
return [];
}
return DB::table('contabilita_situazione_iniziale_ra_links')
->where('cod_stabile_legacy', $codStabileLegacy)
->where('legacy_year', $legacyYear)
->where('cre_deb_preced_id', $creDebPrecedId)
->where('c_d', strtoupper(trim($cd)))
->pluck('registro_ritenuta_acconto_id')
->map(fn ($v) => (int) $v)
->values()
->all();
}
public function linkFatturaElettronica(int $fatturaId): void
{
$fatturaId = (int) $fatturaId;
if ($fatturaId <= 0 || ! $this->selectedCreDebId || ! $this->legacyYear) {
return;
}
if (! Schema::hasTable('contabilita_situazione_iniziale_fe_links')) {
return;
}
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
if ($this->tab !== 'debiti') {
return;
}
$cd = 'D';
DB::table('contabilita_situazione_iniziale_fe_links')->updateOrInsert(
[
'cod_stabile_legacy' => $codStabileLegacy,
'legacy_year' => $this->legacyYear,
'cre_deb_preced_id' => (int) $this->selectedCreDebId,
'c_d' => $cd,
'fattura_elettronica_id' => $fatturaId,
],
[
'stabile_id' => $stabileId,
'updated_at' => now(),
'created_at' => now(),
]
);
$this->reloadDocumenti();
}
public function unlinkFatturaElettronica(int $fatturaId): void
{
$fatturaId = (int) $fatturaId;
if ($fatturaId <= 0 || ! $this->selectedCreDebId || ! $this->legacyYear) {
return;
}
if (! Schema::hasTable('contabilita_situazione_iniziale_fe_links')) {
return;
}
$stabile = $this->getActiveStabile();
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
if ($this->tab !== 'debiti') {
return;
}
$cd = 'D';
DB::table('contabilita_situazione_iniziale_fe_links')
->where('cod_stabile_legacy', $codStabileLegacy)
->where('legacy_year', $this->legacyYear)
->where('cre_deb_preced_id', (int) $this->selectedCreDebId)
->where('c_d', $cd)
->where('fattura_elettronica_id', $fatturaId)
->delete();
$this->reloadDocumenti();
}
public function linkRegistroRa(int $registroId): void
{
$registroId = (int) $registroId;
if ($registroId <= 0 || ! $this->selectedCreDebId || ! $this->legacyYear) {
return;
}
if (! Schema::hasTable('contabilita_situazione_iniziale_ra_links')) {
return;
}
$stabile = $this->getActiveStabile();
$stabileId = $stabile?->id ? (int) $stabile->id : null;
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
if ($this->tab !== 'debiti') {
return;
}
$cd = 'D';
DB::table('contabilita_situazione_iniziale_ra_links')->updateOrInsert(
[
'cod_stabile_legacy' => $codStabileLegacy,
'legacy_year' => $this->legacyYear,
'cre_deb_preced_id' => (int) $this->selectedCreDebId,
'c_d' => $cd,
'registro_ritenuta_acconto_id' => $registroId,
],
[
'stabile_id' => $stabileId,
'updated_at' => now(),
'created_at' => now(),
]
);
$this->reloadDocumenti();
}
public function unlinkRegistroRa(int $registroId): void
{
$registroId = (int) $registroId;
if ($registroId <= 0 || ! $this->selectedCreDebId || ! $this->legacyYear) {
return;
}
if (! Schema::hasTable('contabilita_situazione_iniziale_ra_links')) {
return;
}
$stabile = $this->getActiveStabile();
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
if ($this->tab !== 'debiti') {
return;
}
$cd = 'D';
DB::table('contabilita_situazione_iniziale_ra_links')
->where('cod_stabile_legacy', $codStabileLegacy)
->where('legacy_year', $this->legacyYear)
->where('cre_deb_preced_id', (int) $this->selectedCreDebId)
->where('c_d', $cd)
->where('registro_ritenuta_acconto_id', $registroId)
->delete();
$this->reloadDocumenti();
}
/**
* @return array<string, string>
*/
private function loadVocSpeOptions(?string $codStabileLegacy, ?string $legacyYear): array
{
if (! $codStabileLegacy) {
return [];
}
if (! Schema::connection('gescon_import')->hasTable('voc_spe')) {
return [];
}
$q = DB::connection('gescon_import')->table('voc_spe')->where('cod_stabile', $codStabileLegacy);
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('voc_spe', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$rows = $q
->orderBy('cod')
->limit(300)
->get(['cod', 'descriz']);
$out = [];
foreach ($rows as $r) {
$cod = trim((string) ($r->cod ?? ''));
if ($cod === '') {
continue;
}
$label = trim((string) ($r->descriz ?? ''));
$out[$cod] = $label !== '' ? ($cod . ' — ' . $label) : $cod;
}
return $out;
}
private function loadCreDebPrecedCrudRows(string $codStabileLegacy, ?string $legacyYear, string $cd): array
{
if (! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return [[], []];
}
$cd = strtoupper(trim($cd));
if (! in_array($cd, ['C', 'D'], true)) {
$cd = 'D';
}
$q = DB::connection('gescon_import')->table('cre_deb_preced')
->whereRaw('UPPER(c_d) = ?', [$cd]);
$hasForStabile = DB::connection('gescon_import')->table('cre_deb_preced')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$rows = $q->orderBy('cod_voc')->orderBy('id')->limit(5000)
->get(['id', 'c_d', 'cod_voc', 'des_voce', 'descrizione', 'importo_euro', 'n_stra', 'incluso'])
->map(fn ($r) => [
'id' => (int) $r->id,
'c_d' => (string) ($r->c_d ?? ''),
'cod_voc' => (string) ($r->cod_voc ?? ''),
'des_voce' => (string) ($r->des_voce ?? ''),
'descrizione' => (string) ($r->descrizione ?? ''),
'importo_euro' => (float) ($r->importo_euro ?? 0.0),
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
'incluso' => $r->incluso !== null ? (int) $r->incluso : 1,
])->all();
// Lookup descrizione voce di spesa da voc_spe (stesso MDB): cod_voc -> voc_spe.cod
$vocMap = [];
if (! empty($rows) && Schema::connection('gescon_import')->hasTable('voc_spe')) {
$cods = array_values(array_unique(array_filter(array_map(fn ($r) => trim((string) ($r['cod_voc'] ?? '')), $rows), fn ($v) => $v !== '')));
if (! empty($cods)) {
$vq = DB::connection('gescon_import')->table('voc_spe')->where('cod_stabile', $codStabileLegacy);
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('voc_spe', 'legacy_year')) {
$vq->where('legacy_year', $legacyYear);
}
if (Schema::connection('gescon_import')->hasColumn('voc_spe', 'cod')) {
$vq->whereIn('cod', $cods);
}
foreach ($vq->get(['cod', 'descriz', 'consuntivo_euro', 'preventivo_euro']) as $vr) {
$k = trim((string) ($vr->cod ?? ''));
if ($k === '') {
continue;
}
$vocMap[$k] = [
'descriz' => trim((string) ($vr->descriz ?? '')),
'consuntivo_euro' => $vr->consuntivo_euro !== null ? (float) $vr->consuntivo_euro : null,
'preventivo_euro' => $vr->preventivo_euro !== null ? (float) $vr->preventivo_euro : null,
];
}
}
}
if (! empty($vocMap)) {
foreach ($rows as &$row) {
$cod = trim((string) ($row['cod_voc'] ?? ''));
if ($cod !== '' && isset($vocMap[$cod])) {
$row['voc_spe_descriz'] = (string) ($vocMap[$cod]['descriz'] ?? '');
$row['voc_spe_consuntivo_euro'] = $vocMap[$cod]['consuntivo_euro'] ?? null;
$row['voc_spe_preventivo_euro'] = $vocMap[$cod]['preventivo_euro'] ?? null;
} else {
$row['voc_spe_descriz'] = '';
$row['voc_spe_consuntivo_euro'] = null;
$row['voc_spe_preventivo_euro'] = null;
}
}
unset($row);
} else {
foreach ($rows as &$row) {
$row['voc_spe_descriz'] = '';
$row['voc_spe_consuntivo_euro'] = null;
$row['voc_spe_preventivo_euro'] = null;
}
unset($row);
}
$edit = [];
foreach ($rows as $row) {
$id = (int) ($row['id'] ?? 0);
if ($id <= 0) {
continue;
}
$edit[$id] = [
'cod_voc' => $row['cod_voc'],
'des_voce' => $row['des_voce'],
'descrizione' => $row['descrizione'],
'importo_euro' => $row['importo_euro'],
'n_stra' => $row['n_stra'],
'incluso' => $row['incluso'],
];
}
return [$rows, $edit];
}
private function loadDettTabCrudRows(string $codStabileLegacy, ?string $legacyYear): array
{
if (! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return [[], []];
}
$q = DB::connection('gescon_import')->table('dett_tab')
->where('cod_tab', 'CONG.O')
->where(function ($query) {
$query->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
});
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('dett_tab')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$labels = $this->loadCondominiLabels($codStabileLegacy, $legacyYear);
$rows = $q->orderBy('cod_tab')
->orderBy('n_stra')
->orderByRaw('CAST(id_cond AS UNSIGNED)')
->orderBy('id')
->limit(5000)
->get(['id', 'cod_tab', 'id_cond', 'cond_inquil', 'mm', 'cons_euro', 'n_stra', 'unico'])
->map(fn ($r) => [
'id' => (int) $r->id,
'cod_tab' => (string) ($r->cod_tab ?? ''),
'id_cond' => (string) ($r->id_cond ?? ''),
'cond_label' => (string) ($labels[trim((string) ($r->id_cond ?? ''))] ?? ''),
'cond_inquil' => (string) ($r->cond_inquil ?? ''),
'mm' => $r->mm !== null ? (float) $r->mm : null,
'cons_euro' => $r->cons_euro !== null ? (float) $r->cons_euro : 0.0,
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
'unico' => $r->unico !== null ? (int) $r->unico : 0,
])->all();
$edit = [];
foreach ($rows as $row) {
$id = (int) ($row['id'] ?? 0);
if ($id <= 0) {
continue;
}
$edit[$id] = [
'cod_tab' => $row['cod_tab'],
'id_cond' => $row['id_cond'],
'cond_inquil' => $row['cond_inquil'],
'mm' => $row['mm'],
'cons_euro' => $row['cons_euro'],
'n_stra' => $row['n_stra'],
'unico' => $row['unico'],
];
}
return [$rows, $edit];
}
public function saveCreDebRow(int $id): void
{
if ($id <= 0 || ! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return;
}
$edit = $this->creDebEdit[$id] ?? null;
if (! is_array($edit)) {
return;
}
$importo = $this->parseMoney(is_string($edit['importo_euro'] ?? null) ? (string) $edit['importo_euro'] : null);
if ($importo === null && is_numeric($edit['importo_euro'] ?? null)) {
$importo = (float) $edit['importo_euro'];
}
if ($importo === null) {
$importo = 0.0;
}
DB::connection('gescon_import')->table('cre_deb_preced')
->where('id', $id)
->update([
'cod_voc' => is_string($edit['cod_voc'] ?? null) ? trim((string) $edit['cod_voc']) : null,
'des_voce' => is_string($edit['des_voce'] ?? null) ? trim((string) $edit['des_voce']) : null,
'descrizione' => is_string($edit['descrizione'] ?? null) ? trim((string) $edit['descrizione']) : null,
'importo_euro' => round((float) $importo, 2),
'n_stra' => is_numeric($edit['n_stra'] ?? null) ? (int) $edit['n_stra'] : null,
'incluso' => ! empty($edit['incluso']) ? 1 : 0,
'updated_at' => now(),
]);
$this->reloadData();
}
public function deleteCreDebRow(int $id): void
{
if ($id <= 0 || ! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return;
}
DB::connection('gescon_import')->table('cre_deb_preced')->where('id', $id)->delete();
unset($this->creDebEdit[$id]);
$this->reloadData();
}
public function addCreDebRow(string $cd): void
{
if (! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return;
}
$stabile = $this->getActiveStabile();
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
$cd = strtoupper(trim($cd));
if (! in_array($cd, ['C', 'D'], true)) {
$cd = 'D';
}
$importo = $this->parseMoney(is_string($this->formCreDebAdd['importo_euro'] ?? null) ? (string) $this->formCreDebAdd['importo_euro'] : null);
if ($importo === null) {
return;
}
$row = [
'cod_stabile' => $codStabileLegacy,
'legacy_year' => $this->legacyYear,
'c_d' => $cd,
'cod_voc' => is_string($this->formCreDebAdd['cod_voc'] ?? null) ? trim((string) $this->formCreDebAdd['cod_voc']) : null,
'des_voce' => is_string($this->formCreDebAdd['des_voce'] ?? null) ? trim((string) $this->formCreDebAdd['des_voce']) : null,
'descrizione' => is_string($this->formCreDebAdd['descrizione'] ?? null) ? trim((string) $this->formCreDebAdd['descrizione']) : null,
'importo_euro' => round((float) $importo, 2),
'n_stra' => is_numeric($this->formCreDebAdd['n_stra'] ?? null) ? (int) $this->formCreDebAdd['n_stra'] : null,
'incluso' => ! empty($this->formCreDebAdd['incluso']) ? 1 : 0,
'created_at' => now(),
'updated_at' => now(),
];
$this->editingCreDebId = null;
DB::connection('gescon_import')->table('cre_deb_preced')->insert($row);
$this->formCreDebAdd['cod_voc'] = null;
$this->selectedCreDebId = null;
$this->formCreDebAdd['des_voce'] = null;
$this->formCreDebAdd['descrizione'] = null;
$this->formCreDebAdd['importo_euro'] = null;
$this->formCreDebAdd['n_stra'] = null;
$this->formCreDebAdd['incluso'] = 1;
$this->reloadData();
}
public function saveDettTabRow(int $id): void
{
if ($id <= 0 || ! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return;
}
$edit = $this->dettTabEdit[$id] ?? null;
if (! is_array($edit)) {
return;
}
$cons = $this->parseMoney(is_string($edit['cons_euro'] ?? null) ? (string) $edit['cons_euro'] : null);
if ($cons === null && is_numeric($edit['cons_euro'] ?? null)) {
$cons = (float) $edit['cons_euro'];
}
if ($cons === null) {
$cons = 0.0;
}
DB::connection('gescon_import')->table('dett_tab')
->where('id', $id)
->update([
'cod_tab' => 'CONG.O',
'id_cond' => is_string($edit['id_cond'] ?? null) ? trim((string) $edit['id_cond']) : null,
'cond_inquil' => is_string($edit['cond_inquil'] ?? null) ? trim((string) $edit['cond_inquil']) : null,
'mm' => is_numeric($edit['mm'] ?? null) ? (float) $edit['mm'] : null,
'cons_euro' => round((float) $cons, 2),
'n_stra' => null,
'unico' => ! empty($edit['unico']) ? 1 : 0,
'updated_at' => now(),
]);
$this->reloadData();
}
public function deleteDettTabRow(int $id): void
{
if ($id <= 0 || ! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return;
}
DB::connection('gescon_import')->table('dett_tab')->where('id', $id)->delete();
unset($this->dettTabEdit[$id]);
$this->reloadData();
}
public function addDettTabRow(): void
{
if (! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return;
}
$stabile = $this->getActiveStabile();
$codStabileLegacy = $stabile?->codice_stabile ? (string) $stabile->codice_stabile : null;
if (! $codStabileLegacy) {
return;
}
$codTab = 'CONG.O';
$idCond = is_string($this->formDettTabAdd['id_cond'] ?? null) ? trim((string) $this->formDettTabAdd['id_cond']) : '';
if ($codTab === '' || $idCond === '') {
return;
}
$cons = $this->parseMoney(is_string($this->formDettTabAdd['cons_euro'] ?? null) ? (string) $this->formDettTabAdd['cons_euro'] : null);
if ($cons === null) {
return;
}
$row = [
'cod_tab' => $codTab,
'id_cond' => $idCond,
'cond_inquil' => is_string($this->formDettTabAdd['cond_inquil'] ?? null) ? trim((string) $this->formDettTabAdd['cond_inquil']) : null,
'mm' => is_numeric($this->formDettTabAdd['mm'] ?? null) ? (float) $this->formDettTabAdd['mm'] : null,
'cons_euro' => round((float) $cons, 2),
'n_stra' => null,
'unico' => ! empty($this->formDettTabAdd['unico']) ? 1 : 0,
'created_at' => now(),
'updated_at' => now(),
];
$this->editingDettTabId = null;
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$row['cod_stabile'] = $codStabileLegacy;
}
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$row['legacy_year'] = $this->legacyYear;
}
DB::connection('gescon_import')->table('dett_tab')->insert($row);
$this->formDettTabAdd['id_cond'] = null;
$this->formDettTabAdd['cond_inquil'] = null;
$this->formDettTabAdd['mm'] = null;
$this->formDettTabAdd['cons_euro'] = null;
$this->formDettTabAdd['n_stra'] = null;
$this->formDettTabAdd['unico'] = 0;
$this->reloadData();
}
private function loadLegacyYears(?string $codStabileLegacy): array
{
$years = [];
// Preferisci la mappa "anni" (generale_stabile.mdb) per ottenere l'elenco cartelle in ordine naturale.
if ($codStabileLegacy && Schema::connection('gescon_import')->hasTable('gestioni_annuali')) {
$rows = DB::connection('gescon_import')->table('gestioni_annuali')
->where('cod_stabile', $codStabileLegacy)
->get(['cartella', 'anno_ordinario', 'anno_riscaldamento']);
$mapped = [];
foreach ($rows as $r) {
$cartella = trim((string) ($r->cartella ?? ''));
if ($cartella === '') {
continue;
}
$ordYear = null;
$ao = trim((string) ($r->anno_ordinario ?? ''));
if ($ao !== '' && preg_match('/^(\d{4})$/', $ao, $m)) {
$ordYear = (int) $m[1];
}
$mapped[] = ['cartella' => $cartella, 'ord' => $ordYear ?? 0];
}
usort($mapped, function (array $a, array $b): int {
if ($a['ord'] !== $b['ord']) {
return $a['ord'] <=> $b['ord'];
}
return strcmp((string) $a['cartella'], (string) $b['cartella']);
});
foreach ($mapped as $m) {
$years[] = (string) $m['cartella'];
}
}
if ($codStabileLegacy && Schema::connection('gescon_import')->hasTable('cre_deb_preced') && Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'legacy_year')) {
$years = array_merge($years, DB::connection('gescon_import')->table('cre_deb_preced')
->where('cod_stabile', $codStabileLegacy)
->whereNotNull('legacy_year')
->distinct()
->orderBy('legacy_year')
->pluck('legacy_year')
->map(fn ($v) => (string) $v)
->all());
}
if ($codStabileLegacy && Schema::connection('gescon_import')->hasTable('dett_tab') && Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$q = DB::connection('gescon_import')->table('dett_tab')
->where('cod_tab', 'like', 'CONG.%')
->whereNotNull('legacy_year');
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$q->where('cod_stabile', $codStabileLegacy);
}
$years = array_merge($years, $q->distinct()->orderBy('legacy_year')->pluck('legacy_year')->map(fn ($v) => (string) $v)->all());
}
// Include anni presenti nelle straordinarie anche se non ci sono righe in cre_deb_preced/dett_tab.
if (Schema::connection('gescon_import')->hasTable('straordinarie') && Schema::connection('gescon_import')->hasColumn('straordinarie', 'legacy_year')) {
$q = DB::connection('gescon_import')->table('straordinarie')
->whereNotNull('legacy_year');
if ($codStabileLegacy && Schema::connection('gescon_import')->hasColumn('straordinarie', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('straordinarie')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where(function ($q) use ($codStabileLegacy) {
$q->where('cod_stabile', $codStabileLegacy)
->orWhereNull('cod_stabile')
->orWhere('cod_stabile', '');
});
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
$years = array_merge($years, $q->distinct()->orderBy('legacy_year')->pluck('legacy_year')->map(fn ($v) => (string) $v)->all());
}
$years = array_values(array_unique(array_filter($years, fn ($v) => $v !== '')));
// Se non abbiamo una mappa "anni", ordina in modo consistente.
if (! ($codStabileLegacy && Schema::connection('gescon_import')->hasTable('gestioni_annuali'))) {
sort($years);
}
return $years;
}
private function emptyBilancio(): array
{
return [
'ordinaria' => ['cong_pos' => 0.0, 'cong_neg' => 0.0, 'crediti' => 0.0, 'debiti' => 0.0, 'attivita' => 0.0, 'passivita' => 0.0],
'riscaldamento' => ['cong_pos' => 0.0, 'cong_neg' => 0.0, 'crediti' => 0.0, 'debiti' => 0.0, 'attivita' => 0.0, 'passivita' => 0.0],
'straordinarie' => [],
'tot_attivita' => 0.0,
'tot_passivita' => 0.0,
'finanziario' => 0.0,
'banca' => [
'saldo_manuale' => null,
'saldo_letto' => null,
'delta_manuale' => null,
'delta_letto' => null,
],
];
}
private function defaultDataBilancio(): string
{
$legacyYear = $this->legacyYear;
if ($legacyYear && preg_match('/^\d{4}$/', $legacyYear)) {
$n = (int) $legacyYear;
// Se è già un anno reale (es. 2024), usalo direttamente.
if ($n >= 1900 && $n <= 2100) {
return sprintf('%04d-12-31', $n - 1);
}
// Altrimenti tratta 0001/0002/... come indice esercizio (0001 = 2024).
if ($n >= 1 && $n <= 99) {
$year = 2022 + $n;
return sprintf('%04d-12-31', $year);
}
}
return now()->toDateString();
}
private function loadContiOptions(int $stabileId): array
{
if (! DB::getSchemaBuilder()->hasTable('dati_bancari')) {
return [];
}
return DatiBancari::query()
->where('stabile_id', $stabileId)
->orderBy('id')
->get(['id', 'denominazione_banca', 'iban', 'numero_conto', 'legacy_cod_cassa'])
->mapWithKeys(function (DatiBancari $c): array {
$iban = is_string($c->iban) ? trim((string) $c->iban) : '';
$numeroConto = is_string($c->numero_conto) ? trim((string) ($c->numero_conto ?? '')) : '';
$legacyCodCassa = is_string($c->legacy_cod_cassa) ? strtoupper(trim((string) ($c->legacy_cod_cassa ?? ''))) : '';
$label = trim((string) ($c->denominazione_banca ?? ''));
$identifier = $iban !== '' ? $iban : ($numeroConto !== '' ? $numeroConto : ($legacyCodCassa !== '' ? ('Cassa ' . $legacyCodCassa) : ('Conto #' . $c->id)));
$label = $label !== '' ? ($label . ' · ' . $identifier) : $identifier;
return [(string) $c->id => $label];
})
->all();
}
private function computeBilancio(
int $stabileId,
string $codStabileLegacy,
?string $legacyYear,
string $dataBilancio,
?string $saldoBancaManuale,
array $risorseFinanziarie,
): array {
$ordinaria = $this->computeBucketOrdinaria($codStabileLegacy, $legacyYear);
$riscaldamento = $this->computeBucketRiscaldamento($codStabileLegacy, $legacyYear);
// Straordinarie: cross-year. Identità = (legacy_year, id_stra).
$straEntries = $this->loadStraEntries($codStabileLegacy, $legacyYear);
$stra = [];
foreach ($straEntries as $e) {
$eYear = $e['legacy_year'] ?? null;
$eStra = (int) ($e['n_stra'] ?? 0);
$eKey = (string) ($e['key'] ?? (string) $eStra);
if ($eStra <= 0) {
continue;
}
$bucket = $this->computeBucketStraordinaria($codStabileLegacy, $eYear ?? $legacyYear, $eStra);
$bucket['legacy_year'] = $eYear;
$bucket['key'] = $eKey;
$bucket['label'] = $this->formatStraLabel($eYear, $eStra, (string) ($bucket['titolo'] ?? ''));
$stra[$eKey] = $bucket;
}
// Totali generali (stabile): CREDITI/DEBITI conteggiati UNA SOLA VOLTA,
// mentre i conguagli si sommano per gestione (ORD/RISC/STRA).
$totCreditiUnici = (float) ($ordinaria['crediti'] ?? 0.0);
$totDebitiUnici = (float) ($ordinaria['debiti'] ?? 0.0);
$totCongPos = (float) ($ordinaria['cong_pos'] ?? 0.0) + (float) ($riscaldamento['cong_pos'] ?? 0.0);
$totCongNeg = (float) ($ordinaria['cong_neg'] ?? 0.0) + (float) ($riscaldamento['cong_neg'] ?? 0.0);
$totAtt = $totCreditiUnici + $totCongPos;
$totPas = $totDebitiUnici + $totCongNeg;
foreach ($stra as $row) {
$totAtt += (float) ($row['cong_pos'] ?? 0.0) + (float) ($row['crediti'] ?? 0.0);
$totPas += (float) ($row['cong_neg'] ?? 0.0) + (float) ($row['debiti'] ?? 0.0);
}
$totAtt = round($totAtt, 2);
$totPas = round($totPas, 2);
$fin = round($totAtt - $totPas, 2);
$saldoLetto = $this->computeSaldoBancaTotaleLetto($risorseFinanziarie);
$saldoManualeVal = $this->parseMoney($saldoBancaManuale);
return [
'ordinaria' => $ordinaria,
'riscaldamento' => $riscaldamento,
'straordinarie' => $stra,
'tot_attivita' => $totAtt,
'tot_passivita' => $totPas,
'finanziario' => $fin,
'banca' => [
'saldo_manuale' => $saldoManualeVal,
'saldo_letto' => $saldoLetto,
'delta_manuale' => $saldoManualeVal !== null ? round($saldoManualeVal - $fin, 2) : null,
'delta_letto' => $saldoLetto !== null ? round($saldoLetto - $fin, 2) : null,
],
];
}
private function computeBucketOrdinaria(string $codStabileLegacy, ?string $legacyYear): array
{
$crediti = $this->sumCreDebPreced($codStabileLegacy, $legacyYear, 'C', 'non_stra')
+ $this->sumManualVoci('ordinaria', 'crediti', null, $legacyYear);
$debiti = $this->sumCreDebPreced($codStabileLegacy, $legacyYear, 'D', 'non_stra')
+ $this->sumManualVoci('ordinaria', 'debiti', null, $legacyYear);
[$congPos, $congNeg] = $this->sumConguagli($codStabileLegacy, $legacyYear, 'CONG.O', 'non_stra');
$att = round($crediti + $congPos, 2);
$pas = round($debiti + $congNeg, 2);
return [
'cong_pos' => $congPos,
'cong_neg' => $congNeg,
'crediti' => $crediti,
'debiti' => $debiti,
'attivita' => $att,
'passivita' => $pas,
];
}
private function computeBucketRiscaldamento(string $codStabileLegacy, ?string $legacyYear): array
{
// IMPORTANTE:
// `cre_deb_preced` (crediti/debiti pregressi) non distingue la gestione (ordinaria vs riscaldamento).
// Per evitare duplicazioni percepite in UI, i Crediti/Debiti "pregressi" vengono mostrati SOLO in Ordinaria.
// Qui teniamo esclusivamente i conguagli CONG.R (e gli eventuali manuali riscaldamento, se presenti).
$crediti = 0.0;
$debiti = 0.0;
[$congPos, $congNeg] = $this->sumConguagli($codStabileLegacy, $legacyYear, 'CONG.R', 'non_stra');
$att = round($crediti + $congPos, 2);
$pas = round($debiti + $congNeg, 2);
return [
'cong_pos' => $congPos,
'cong_neg' => $congNeg,
'crediti' => $crediti,
'debiti' => $debiti,
'attivita' => $att,
'passivita' => $pas,
];
}
private function computeBucketStraordinaria(string $codStabileLegacy, ?string $legacyYear, int $nStra): array
{
$crediti = $this->sumCreDebPreced($codStabileLegacy, $legacyYear, 'C', $nStra)
+ $this->sumManualVoci('straordinaria', 'crediti', $nStra, $legacyYear);
$debiti = $this->sumCreDebPreced($codStabileLegacy, $legacyYear, 'D', $nStra)
+ $this->sumManualVoci('straordinaria', 'debiti', $nStra, $legacyYear);
[$congPos, $congNeg] = $this->sumConguagli($codStabileLegacy, $legacyYear, null, $nStra);
$titolo = $this->resolveStraordinariaTitolo($codStabileLegacy, $legacyYear, $nStra);
$att = round($crediti + $congPos, 2);
$pas = round($debiti + $congNeg, 2);
return [
'n_stra' => $nStra,
'titolo' => $titolo,
'cong_pos' => $congPos,
'cong_neg' => $congNeg,
'crediti' => $crediti,
'debiti' => $debiti,
'attivita' => $att,
'passivita' => $pas,
];
}
private function formatStraLabel(?string $legacyYear, int $nStra, string $titolo): string
{
$titolo = trim($titolo);
$prefix = '';
if ($legacyYear) {
$label = $this->legacyYearLabels[$legacyYear] ?? null;
if (is_string($label) && trim($label) !== '') {
$prefix = trim($label) . ' · ';
}
}
$base = 'Straordinaria #' . $nStra;
if ($titolo === '') {
return $prefix . $base;
}
return $prefix . $base . ' · ' . $titolo;
}
private function legacyYearToRealYear(?string $legacyYear): ?int
{
if (! $legacyYear || ! preg_match('/^\d{4}$/', $legacyYear)) {
return null;
}
$n = (int) $legacyYear;
if ($n >= 1900 && $n <= 2100) {
return $n;
}
return null;
}
private function sumManualVoci(string $gestione, string $categoria, ?int $nStra, ?string $legacyYear): float
{
if (! DB::getSchemaBuilder()->hasTable('contabilita_situazione_iniziale_voci')) {
return 0.0;
}
$stabileId = $this->getActiveStabile()?->id;
if (! $stabileId) {
return 0.0;
}
$q = DB::table('contabilita_situazione_iniziale_voci')
->where('stabile_id', (int) $stabileId)
->where('gestione', $gestione)
->where('categoria', $categoria)
->where('incluso', 1);
if ($legacyYear !== null && $legacyYear !== '') {
$q->where('legacy_year', $legacyYear);
}
if ($gestione === 'straordinaria') {
$q->where('n_stra', $nStra);
} else {
$q->whereNull('n_stra');
}
return round((float) ($q->sum('importo_euro') ?? 0.0), 2);
}
private function sumManualConguagli(?string $legacyYear, ?string $codTab, int|string $straFilter): array
{
if (! DB::getSchemaBuilder()->hasTable('contabilita_situazione_iniziale_conguagli')) {
return [0.0, 0.0];
}
$stabileId = $this->getActiveStabile()?->id;
if (! $stabileId) {
return [0.0, 0.0];
}
$q = DB::table('contabilita_situazione_iniziale_conguagli')
->where('stabile_id', (int) $stabileId)
->where('incluso', 1);
if ($legacyYear !== null && $legacyYear !== '') {
$q->where('legacy_year', $legacyYear);
}
if ($codTab) {
$q->where('cod_tab', $codTab);
}
if ($straFilter === 'non_stra') {
$q->whereNull('n_stra');
} elseif (is_int($straFilter) && $straFilter > 0) {
$q->where('n_stra', $straFilter);
}
$pos = (float) (clone $q)->where('cons_euro', '>', 0)->sum('cons_euro');
$neg = (float) (clone $q)->where('cons_euro', '<', 0)->sum('cons_euro');
return [round($pos, 2), round(abs($neg), 2)];
}
private function resolveStraordinariaTitolo(string $codStabileLegacy, ?string $legacyYear, int $nStra): string
{
if (! Schema::connection('gescon_import')->hasTable('straordinarie')) {
return 'Straordinaria #' . $nStra;
}
$row = null;
$applyCodStabileFallback = function ($q) use ($codStabileLegacy) {
if (! Schema::connection('gescon_import')->hasColumn('straordinarie', 'cod_stabile')) {
return $q;
}
$hasForStabile = DB::connection('gescon_import')->table('straordinarie')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
// Include anche righe con cod_stabile vuoto: alcuni import parziali le lasciano NULL/''.
return $q->where(function ($q) use ($codStabileLegacy) {
$q->where('cod_stabile', $codStabileLegacy)
->orWhereNull('cod_stabile')
->orWhere('cod_stabile', '');
});
}
return $q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
};
// Preferisci l'anno selezionato se presente (per compatibilità con import per-cartella),
// ma fai fallback cross-year così la UI può mostrare SEMPRE tutte le straordinarie.
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('straordinarie', 'legacy_year')) {
$q = DB::connection('gescon_import')->table('straordinarie');
$q = $applyCodStabileFallback($q);
$row = $q
->where('id_stra', $nStra)
->where('legacy_year', $legacyYear)
->first(['codice', 'descriz_prev_cons', 'descriz_ricev']);
}
if (! $row) {
$q = DB::connection('gescon_import')->table('straordinarie');
$q = $applyCodStabileFallback($q);
$row = $q
->where('id_stra', $nStra)
->orderByDesc(Schema::connection('gescon_import')->hasColumn('straordinarie', 'legacy_year') ? 'legacy_year' : 'id_stra')
->first(['codice', 'descriz_prev_cons', 'descriz_ricev']);
}
$title = '';
if ($row) {
$title = trim((string) ($row->descriz_prev_cons ?? ''));
if ($title === '') {
$title = trim((string) ($row->descriz_ricev ?? ''));
}
if ($title === '') {
$title = trim((string) ($row->codice ?? ''));
}
}
return $title !== '' ? $title : ('Straordinaria #' . $nStra);
}
/**
* @return array<int, array{key: string, legacy_year: string|null, n_stra: int}>
*/
private function loadStraEntries(string $codStabileLegacy, ?string $legacyYear): array
{
$entries = [];
$add = function (?string $y, int $n) use (&$entries) {
if ($n <= 0) {
return;
}
$yy = $y !== null && $y !== '' ? (string) $y : null;
$key = $yy ? ($yy . ':' . $n) : (string) $n;
$entries[$key] = ['key' => $key, 'legacy_year' => $yy, 'n_stra' => $n];
};
if (Schema::connection('gescon_import')->hasTable('cre_deb_preced') && Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'n_stra')) {
$q = DB::connection('gescon_import')->table('cre_deb_preced')
->whereNotNull('n_stra')
->where('n_stra', '>', 0);
// Include anche righe con cod_stabile vuoto quando esistono righe per lo stabile.
$hasForStabile = DB::connection('gescon_import')->table('cre_deb_preced')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where(function ($q) use ($codStabileLegacy) {
$q->where('cod_stabile', $codStabileLegacy)
->orWhereNull('cod_stabile')
->orWhere('cod_stabile', '');
});
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
if (Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'incluso')) {
$q->where('incluso', 1);
}
// IMPORTANTE: NON filtrare per legacy_year: vogliamo vedere tutte le straordinarie cross-year.
if (Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'legacy_year')) {
foreach ($q->distinct()->get(['legacy_year', 'n_stra']) as $r) {
$add(is_string($r->legacy_year ?? null) ? (string) $r->legacy_year : null, (int) ($r->n_stra ?? 0));
}
} else {
foreach ($q->distinct()->orderBy('n_stra')->pluck('n_stra') as $v) {
$add($legacyYear, (int) $v);
}
}
}
if (Schema::connection('gescon_import')->hasTable('dett_tab') && Schema::connection('gescon_import')->hasColumn('dett_tab', 'n_stra')) {
$q = DB::connection('gescon_import')->table('dett_tab')
->where('cod_tab', 'like', 'CONG.%')
->whereNotNull('n_stra')
->where('n_stra', '>', 0);
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('dett_tab')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where(function ($q) use ($codStabileLegacy) {
$q->where('cod_stabile', $codStabileLegacy)
->orWhereNull('cod_stabile')
->orWhere('cod_stabile', '');
});
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
// IMPORTANTE: NON filtrare per legacy_year: vogliamo vedere tutte le straordinarie cross-year.
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
foreach ($q->distinct()->get(['legacy_year', 'n_stra']) as $r) {
$add(is_string($r->legacy_year ?? null) ? (string) $r->legacy_year : null, (int) ($r->n_stra ?? 0));
}
} else {
foreach ($q->distinct()->orderBy('n_stra')->pluck('n_stra') as $v) {
$add($legacyYear, (int) $v);
}
}
}
// Include tutte le straordinarie dichiarate nel MDB (tabella gescon_import.straordinarie), anche se importi = 0.
if (Schema::connection('gescon_import')->hasTable('straordinarie') && Schema::connection('gescon_import')->hasColumn('straordinarie', 'id_stra')) {
$q = DB::connection('gescon_import')->table('straordinarie')
->whereNotNull('id_stra')
->where('id_stra', '>', 0);
// Fallback: alcuni import da singolo_anno.mdb non popolano cod_stabile.
if (Schema::connection('gescon_import')->hasColumn('straordinarie', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('straordinarie')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where(function ($q) use ($codStabileLegacy) {
$q->where('cod_stabile', $codStabileLegacy)
->orWhereNull('cod_stabile')
->orWhere('cod_stabile', '');
});
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
// IMPORTANTE: NON filtrare per legacy_year.
// Le straordinarie sono "tabelle" trasversali: vogliamo vederle tutte (es. 4 in 0001 + 5 in 0003 = 9).
if (Schema::connection('gescon_import')->hasColumn('straordinarie', 'legacy_year')) {
foreach ($q->distinct()->get(['legacy_year', 'id_stra']) as $r) {
$add(is_string($r->legacy_year ?? null) ? (string) $r->legacy_year : null, (int) ($r->id_stra ?? 0));
}
} else {
foreach ($q->distinct()->orderBy('id_stra')->pluck('id_stra') as $v) {
$add($legacyYear, (int) $v);
}
}
}
$list = array_values($entries);
usort($list, function (array $a, array $b): int {
$ya = (string) ($a['legacy_year'] ?? '');
$yb = (string) ($b['legacy_year'] ?? '');
if ($ya !== $yb) {
return $ya <=> $yb;
}
return ((int) $a['n_stra']) <=> ((int) $b['n_stra']);
});
return $list;
}
private function sumCreDebPreced(string $codStabileLegacy, ?string $legacyYear, string $cd, int|string $straFilter): float
{
if (! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return 0.0;
}
$q = DB::connection('gescon_import')->table('cre_deb_preced')
->whereRaw('UPPER(c_d) = ?', [strtoupper($cd)]);
// Fallback: alcuni import da singolo_anno.mdb non popolano cod_stabile.
// In quel caso, se non esistono righe per lo stabile attivo, usa le righe con cod_stabile NULL/vuoto.
$hasForStabile = DB::connection('gescon_import')->table('cre_deb_preced')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
if (Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'incluso')) {
$q->where('incluso', 1);
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
if ($straFilter === 'non_stra') {
$q->where(function ($q) {
$q->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
});
} elseif (is_int($straFilter) && $straFilter > 0) {
$q->where('n_stra', $straFilter);
}
return round((float) ($q->sum('importo_euro') ?? 0.0), 2);
}
private function sumConguagli(string $codStabileLegacy, ?string $legacyYear, ?string $codTab, int|string $straFilter): array
{
if (! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return [0.0, 0.0];
}
$q = DB::connection('gescon_import')->table('dett_tab');
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('dett_tab')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
if ($codTab) {
$q->where('cod_tab', $codTab);
}
if ($straFilter === 'non_stra') {
$q->where(function ($q) {
$q->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
});
} elseif (is_int($straFilter) && $straFilter > 0) {
$q->where('n_stra', $straFilter);
}
$pos = (float) (clone $q)->where('cons_euro', '>', 0)->sum('cons_euro');
$neg = (float) (clone $q)->where('cons_euro', '<', 0)->sum('cons_euro');
return [round($pos, 2), round(abs($neg), 2)];
}
private function computeRisorseFinanziarie(int $stabileId, string $date): array
{
if (! DB::getSchemaBuilder()->hasTable('contabilita_movimenti_banca')) {
return [];
}
if (! DB::getSchemaBuilder()->hasTable('dati_bancari')) {
return [];
}
$conti = DatiBancari::query()
->where('stabile_id', $stabileId)
->when(Schema::hasColumn('dati_bancari', 'stato_conto'), function ($query) {
$query->where(function ($query) {
$query->whereNull('stato_conto')->orWhere('stato_conto', '!=', 'chiuso');
});
})
->orderBy('id')
->get(['id', 'denominazione_banca', 'iban', 'numero_conto', 'legacy_cod_cassa', 'saldo_iniziale', 'data_saldo_iniziale']);
if ($conti->isEmpty()) {
return [];
}
$dateC = Carbon::parse($date);
$out = [];
foreach ($conti as $conto) {
$iban = is_string($conto->iban) ? trim((string) $conto->iban) : '';
$numeroConto = is_string($conto->numero_conto) ? trim((string) ($conto->numero_conto ?? '')) : '';
$legacyCodCassa = is_string($conto->legacy_cod_cassa) ? strtoupper(trim((string) ($conto->legacy_cod_cassa ?? ''))) : '';
$saldo = $this->resolveContoSaldoAllaData($stabileId, $conto, $dateC);
$label = trim((string) ($conto->denominazione_banca ?? ''));
$identifier = $iban !== '' ? $iban : ($numeroConto !== '' ? $numeroConto : ($legacyCodCassa !== '' ? ('Cassa ' . $legacyCodCassa) : ('Conto #' . $conto->id)));
$label = $label !== '' ? ($label . ' · ' . $identifier) : $identifier;
$out[] = [
'conto_id' => (int) $conto->id,
'iban' => $iban,
'label' => $label,
'saldo' => $saldo,
];
}
return $out;
}
private function resolveContoSaldoAllaData(int $stabileId, DatiBancari $conto, Carbon $date): float
{
$contoId = (int) $conto->id;
$snapshot = SaldoConto::query()
->where('stabile_id', $stabileId)
->where('conto_id', $contoId)
->whereDate('data_saldo', '<=', $date->toDateString())
->orderByDesc('data_saldo')
->orderByDesc('id')
->first(['data_saldo', 'saldo']);
if ($snapshot) {
$baseDate = $snapshot->data_saldo instanceof Carbon
? $snapshot->data_saldo->copy()->startOfDay()
: Carbon::parse((string) $snapshot->data_saldo)->startOfDay();
$delta = (float) (MovimentoBanca::query()
->where('stabile_id', $stabileId)
->where('conto_id', $contoId)
->whereDate('data', '>', $baseDate->toDateString())
->whereDate('data', '<=', $date->toDateString())
->sum('importo') ?? 0.0);
return round((float) ($snapshot->saldo ?? 0.0) + $delta, 2);
}
$saldoBase = is_numeric($conto->saldo_iniziale) ? (float) $conto->saldo_iniziale : 0.0;
$dataSaldoIniziale = $conto->data_saldo_iniziale instanceof Carbon
? $conto->data_saldo_iniziale->copy()->startOfDay()
: ($conto->data_saldo_iniziale ? Carbon::parse((string) $conto->data_saldo_iniziale)->startOfDay() : null);
$movimenti = MovimentoBanca::query()
->where('stabile_id', $stabileId)
->where('conto_id', $contoId);
if ($dataSaldoIniziale instanceof Carbon) {
if ($dataSaldoIniziale->lte($date)) {
$delta = (float) (clone $movimenti)
->whereDate('data', '>', $dataSaldoIniziale->toDateString())
->whereDate('data', '<=', $date->toDateString())
->sum('importo');
return round($saldoBase + $delta, 2);
}
$delta = (float) (clone $movimenti)
->whereDate('data', '>', $date->toDateString())
->whereDate('data', '<=', $dataSaldoIniziale->toDateString())
->sum('importo');
return round($saldoBase - $delta, 2);
}
$delta = (float) (clone $movimenti)
->whereDate('data', '<=', $date->toDateString())
->sum('importo');
return round($saldoBase + $delta, 2);
}
private function computeSaldoBancaTotaleLetto(array $risorseFinanziarie): ?float
{
if ($risorseFinanziarie === []) {
return null;
}
$tot = 0.0;
foreach ($risorseFinanziarie as $r) {
$tot += (float) ($r['saldo'] ?? 0.0);
}
return round($tot, 2);
}
private function resolveSaldoOffsetDaSaldi(int $stabileId, int $contoId, string $iban, Carbon $date): float
{
if (! DB::getSchemaBuilder()->hasTable('contabilita_saldi_conti')) {
return 0.0;
}
$match = SaldoConto::query()
->where('stabile_id', $stabileId)
->where('conto_id', $contoId)
->where('iban', $iban)
->whereDate('data_saldo', '<=', $date->toDateString())
->orderByDesc('data_saldo')
->orderByDesc('id')
->first(['id', 'data_saldo', 'saldo']);
if (! $match) {
return 0.0;
}
$saldoIniziale = $this->resolveSaldoInizialeDaDatiBancari($stabileId, $contoId, $iban);
$sumToDateSaldo = (float) (MovimentoBanca::query()
->where('stabile_id', $stabileId)
->where('conto_id', $contoId)
->where('iban', $iban)
->whereDate('data', '<=', $match->data_saldo->toDateString())
->sum('importo') ?? 0.0);
$saldoAttesoAllaDataSaldo = is_numeric($match->saldo) ? (float) $match->saldo : 0.0;
$saldoCalcolatoAllaDataSaldo = $saldoIniziale + $sumToDateSaldo;
return (float) ($saldoAttesoAllaDataSaldo - $saldoCalcolatoAllaDataSaldo);
}
private function resolveSaldoInizialeDaDatiBancari(int $stabileId, int $contoId, string $iban): float
{
$row = DatiBancari::query()
->where('stabile_id', $stabileId)
->where('id', $contoId)
->where('iban', $iban)
->first(['saldo_iniziale']);
return $row && is_numeric($row->saldo_iniziale) ? (float) $row->saldo_iniziale : 0.0;
}
private function parseMoney(?string $value): ?float
{
if (! is_string($value)) {
return null;
}
$v = trim($value);
if ($v === '') {
return null;
}
$v = str_replace(['€', ' '], '', $v);
// Convert Italian format "1.234,56" to "1234.56".
$v = str_replace('.', '', $v);
$v = str_replace(',', '.', $v);
if (! is_numeric($v)) {
return null;
}
return round((float) $v, 2);
}
private function reloadDettaglio(string $codStabileLegacy, ?string $legacyYear): void
{
$this->detailMeta = [];
$this->detailRows = [];
if ($this->tab !== 'dettaglio' || ! $this->dett) {
return;
}
$d = $this->dett;
if (preg_match('/^(crediti|debiti)_(ordinaria|riscaldamento)$/', $d, $m)) {
$cd = $m[1] === 'crediti' ? 'C' : 'D';
$bucket = $m[2];
$straFilter = $bucket === 'ordinaria' ? 'non_stra' : 'none';
$this->detailMeta = [
'tipo' => 'cre_deb_preced',
'titolo' => strtoupper($m[1]) . ' · ' . strtoupper($bucket),
];
$this->detailRows = $this->loadCreDebPrecedRows($codStabileLegacy, $legacyYear, $cd, $straFilter);
return;
}
if (preg_match('/^(crediti|debiti)_stra_(.+)$/', $d, $m)) {
$cd = $m[1] === 'crediti' ? 'C' : 'D';
[$detailYear, $nStra] = $this->parseStraKey($m[2]);
$effectiveYear = $detailYear ?? $legacyYear;
$this->detailMeta = [
'tipo' => 'cre_deb_preced',
'titolo' => strtoupper($m[1]) . ' · ' . $this->formatStraLabel($detailYear, (int) $nStra, ''),
];
$this->detailRows = $this->loadCreDebPrecedRows($codStabileLegacy, $effectiveYear, $cd, (int) $nStra);
return;
}
if (preg_match('/^cong_(pos|neg)_(ordinaria|riscaldamento)$/', $d, $m)) {
$sign = $m[1];
$bucket = $m[2];
$codTab = $bucket === 'ordinaria' ? 'CONG.O' : 'CONG.R';
$this->detailMeta = [
'tipo' => 'dett_tab',
'titolo' => 'CONGUAGLI ' . ($sign === 'pos' ? '(+)' : '(-)') . ' · ' . strtoupper($bucket) . ' · ' . $codTab,
];
$this->detailRows = $this->loadDettTabRows($codStabileLegacy, $legacyYear, $codTab, 'non_stra', $sign);
return;
}
if (preg_match('/^cong_(pos|neg)_stra_(.+)$/', $d, $m)) {
$sign = $m[1];
[$detailYear, $nStra] = $this->parseStraKey($m[2]);
$effectiveYear = $detailYear ?? $legacyYear;
$this->detailMeta = [
'tipo' => 'dett_tab',
'titolo' => 'CONGUAGLI ' . ($sign === 'pos' ? '(+)' : '(-)') . ' · ' . $this->formatStraLabel($detailYear, (int) $nStra, ''),
];
$this->detailRows = $this->loadDettTabRows($codStabileLegacy, $effectiveYear, null, (int) $nStra, $sign);
return;
}
$this->detailMeta = [
'tipo' => 'unknown',
'titolo' => 'Dettaglio non riconosciuto',
];
}
private function loadCreDebPrecedRows(string $codStabileLegacy, ?string $legacyYear, string $cd, int|string $straFilter): array
{
if (! Schema::connection('gescon_import')->hasTable('cre_deb_preced')) {
return [];
}
$q = DB::connection('gescon_import')->table('cre_deb_preced')
->whereRaw('UPPER(c_d) = ?', [strtoupper($cd)]);
$hasForStabile = DB::connection('gescon_import')->table('cre_deb_preced')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
if (Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'incluso')) {
$q->where('incluso', 1);
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('cre_deb_preced', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
if ($straFilter === 'non_stra') {
$q->where(function ($q) {
$q->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
});
} elseif (is_int($straFilter) && $straFilter > 0) {
$q->where('n_stra', $straFilter);
} elseif ($straFilter === 'none') {
// Nessun filtro: restituisce vuoto per bucket senza criterio.
return [];
}
return $q
->orderBy('cod_voc')
->limit(5000)
->get(['c_d', 'cod_voc', 'des_voce', 'descrizione', 'importo_euro', 'n_stra'])
->map(fn ($r) => [
'c_d' => (string) ($r->c_d ?? ''),
'cod_voc' => (string) ($r->cod_voc ?? ''),
'des_voce' => (string) ($r->des_voce ?? ''),
'descrizione' => (string) ($r->descrizione ?? ''),
'importo_euro' => (float) ($r->importo_euro ?? 0),
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
])
->all();
}
private function loadDettTabRows(string $codStabileLegacy, ?string $legacyYear, ?string $codTab, int|string $straFilter, string $sign): array
{
if (! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return [];
}
$labels = $this->loadCondominiLabels($codStabileLegacy, $legacyYear);
$q = DB::connection('gescon_import')->table('dett_tab')
->where('cod_tab', 'like', 'CONG.%');
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$hasForStabile = DB::connection('gescon_import')->table('dett_tab')
->where('cod_stabile', $codStabileLegacy)
->limit(1)
->exists();
if ($hasForStabile) {
$q->where('cod_stabile', $codStabileLegacy);
} else {
$q->where(function ($q) {
$q->whereNull('cod_stabile')->orWhere('cod_stabile', '');
});
}
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
if ($codTab) {
$q->where('cod_tab', $codTab);
}
if ($straFilter === 'non_stra') {
$q->where(function ($q) {
$q->whereNull('n_stra')->orWhere('n_stra', '<=', 0);
});
} elseif (is_int($straFilter) && $straFilter > 0) {
$q->where('n_stra', $straFilter);
}
if ($sign === 'pos') {
$q->where('cons_euro', '>', 0);
} else {
$q->where('cons_euro', '<', 0);
}
return $q
->orderBy('cod_tab')
->orderBy('id_cond')
->limit(5000)
->get(['cod_tab', 'id_cond', 'cond_inquil', 'mm', 'cons_euro', 'n_stra', 'unico', 'proviene_ors', 'proviene_n_stra', 'proviene_eserc'])
->map(fn ($r) => [
'cod_tab' => (string) ($r->cod_tab ?? ''),
'id_cond' => (string) ($r->id_cond ?? ''),
'cond_label' => (string) ($labels[trim((string) ($r->id_cond ?? ''))] ?? ''),
'cond_inquil' => (string) ($r->cond_inquil ?? ''),
'mm' => $r->mm !== null ? (float) $r->mm : null,
'cons_euro' => (float) ($r->cons_euro ?? 0),
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
'unico' => (bool) ($r->unico ?? false),
'proviene_ors' => (string) ($r->proviene_ors ?? ''),
'proviene_n_stra' => $r->proviene_n_stra !== null ? (int) $r->proviene_n_stra : null,
'proviene_eserc' => (string) ($r->proviene_eserc ?? ''),
])
->all();
}
/**
* @return array{0: string|null, 1: int} [legacy_year, n_stra]
*/
private function parseStraKey(string $raw): array
{
$raw = trim($raw);
if ($raw === '') {
return [null, 0];
}
if (preg_match('/^(\d{4})\:(\d+)$/', $raw, $m)) {
return [$m[1], (int) $m[2]];
}
if (ctype_digit($raw)) {
return [null, (int) $raw];
}
return [null, 0];
}
private function computeConguagliExcel(string $codStabileLegacy, ?string $legacyYear): array
{
if (! Schema::connection('gescon_import')->hasTable('dett_tab')) {
return [[], []];
}
$q = DB::connection('gescon_import')->table('dett_tab')
->where('cod_tab', 'like', 'CONG.%');
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'cod_stabile')) {
$q->where('cod_stabile', $codStabileLegacy);
}
if ($legacyYear && Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
$q->where('legacy_year', $legacyYear);
}
$labels = $this->loadCondominiLabels($codStabileLegacy, $legacyYear);
$rows = $q
->select([
'id_cond',
'cod_tab',
'n_stra',
DB::raw('SUM(cons_euro) as totale'),
])
->groupBy('id_cond', 'cod_tab', 'n_stra')
->orderByRaw('CAST(id_cond AS UNSIGNED)')
->get()
->map(fn ($r) => [
'id_cond' => (string) ($r->id_cond ?? ''),
'cond_label' => (string) ($labels[trim((string) ($r->id_cond ?? ''))] ?? ''),
'cod_tab' => (string) ($r->cod_tab ?? ''),
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
'totale' => (float) ($r->totale ?? 0.0),
])
->all();
// Merge manual conguagli
if (DB::getSchemaBuilder()->hasTable('contabilita_situazione_iniziale_conguagli')) {
$stabileId = $this->getActiveStabile()?->id;
if ($stabileId) {
$mq = DB::table('contabilita_situazione_iniziale_conguagli')
->where('stabile_id', (int) $stabileId)
->where('incluso', 1);
if ($legacyYear) {
$mq->where('legacy_year', $legacyYear);
}
$manual = $mq
->select([
'id_cond',
'cod_tab',
'n_stra',
DB::raw('SUM(cons_euro) as totale'),
])
->groupBy('id_cond', 'cod_tab', 'n_stra')
->get()
->map(fn ($r) => [
'id_cond' => (string) ($r->id_cond ?? ''),
'cod_tab' => (string) ($r->cod_tab ?? ''),
'n_stra' => $r->n_stra !== null ? (int) $r->n_stra : null,
'totale' => (float) ($r->totale ?? 0.0),
])
->all();
// Sum into existing keys
$acc = [];
foreach ($rows as $r) {
$k = $r['id_cond'] . '|' . $r['cod_tab'] . '|' . (string) ($r['n_stra'] ?? '');
$acc[$k] = $r;
}
foreach ($manual as $r) {
$k = $r['id_cond'] . '|' . $r['cod_tab'] . '|' . (string) ($r['n_stra'] ?? '');
if (isset($acc[$k])) {
$acc[$k]['totale'] = (float) ($acc[$k]['totale'] ?? 0.0) + (float) ($r['totale'] ?? 0.0);
} else {
$acc[$k] = $r;
}
}
$rows = array_values($acc);
}
}
$cols = [];
foreach ($rows as $r) {
$key = $r['cod_tab'] . '|' . (string) ($r['n_stra'] ?? '');
$cols[$key] = [
'key' => $key,
'label' => $r['cod_tab'] . (($r['n_stra'] ?? null) ? (' #' . $r['n_stra']) : ''),
'cod_tab' => $r['cod_tab'],
'n_stra' => $r['n_stra'],
];
}
$cols = array_values($cols);
usort($cols, fn ($a, $b) => strcmp((string) $a['label'], (string) $b['label']));
$matrix = [];
foreach ($rows as $r) {
$idCond = $r['id_cond'] ?: '—';
if (! isset($matrix[$idCond])) {
$matrix[$idCond] = ['id_cond' => $idCond, 'cells' => []];
}
$key = $r['cod_tab'] . '|' . (string) ($r['n_stra'] ?? '');
$matrix[$idCond]['cells'][$key] = (float) ($r['totale'] ?? 0.0);
}
$out = array_values($matrix);
usort($out, fn ($a, $b) => (int) ($a['id_cond'] ?? 0) <=> (int) ($b['id_cond'] ?? 0));
return [$cols, $out];
}
public function addVoceManuale(): void
{
$stabileId = $this->getActiveStabile()?->id;
if (! $stabileId) {
return;
}
if (! DB::getSchemaBuilder()->hasTable('contabilita_situazione_iniziale_voci')) {
return;
}
$gestione = is_string($this->formVoce['gestione'] ?? null) ? trim((string) $this->formVoce['gestione']) : '';
$categoria = is_string($this->formVoce['categoria'] ?? null) ? trim((string) $this->formVoce['categoria']) : '';
$gestione = in_array($gestione, ['ordinaria', 'riscaldamento', 'straordinaria'], true) ? $gestione : 'ordinaria';
$categoria = in_array($categoria, ['crediti', 'debiti'], true) ? $categoria : 'crediti';
$nStra = $gestione === 'straordinaria' && is_numeric($this->formVoce['n_stra'] ?? null) ? (int) $this->formVoce['n_stra'] : null;
if ($gestione === 'straordinaria' && (! $nStra || $nStra <= 0)) {
return;
}
$importo = $this->parseMoney(is_string($this->formVoce['importo_euro'] ?? null) ? (string) $this->formVoce['importo_euro'] : null);
if ($importo === null) {
return;
}
DB::table('contabilita_situazione_iniziale_voci')->insert([
'stabile_id' => (int) $stabileId,
'legacy_year' => $this->legacyYear,
'gestione' => $gestione,
'n_stra' => $gestione === 'straordinaria' ? $nStra : null,
'categoria' => $categoria,
'codice' => is_string($this->formVoce['codice'] ?? null) ? trim((string) $this->formVoce['codice']) : null,
'descrizione' => is_string($this->formVoce['descrizione'] ?? null) ? trim((string) $this->formVoce['descrizione']) : null,
'importo_euro' => $importo,
'incluso' => 1,
'origine' => 'manuale',
'created_at' => now(),
'updated_at' => now(),
]);
$this->formVoce['codice'] = null;
$this->formVoce['descrizione'] = null;
$this->formVoce['importo_euro'] = null;
$this->reloadData();
}
public function addConguaglioManuale(): void
{
$stabileId = $this->getActiveStabile()?->id;
if (! $stabileId) {
return;
}
if (! DB::getSchemaBuilder()->hasTable('contabilita_situazione_iniziale_conguagli')) {
return;
}
$codTab = is_string($this->formConguaglio['cod_tab'] ?? null) ? trim((string) $this->formConguaglio['cod_tab']) : '';
if ($codTab === '') {
return;
}
$idCond = is_string($this->formConguaglio['id_cond'] ?? null) ? trim((string) $this->formConguaglio['id_cond']) : '';
if ($idCond === '') {
return;
}
$nStra = is_numeric($this->formConguaglio['n_stra'] ?? null) ? (int) $this->formConguaglio['n_stra'] : null;
$cons = $this->parseMoney(is_string($this->formConguaglio['cons_euro'] ?? null) ? (string) $this->formConguaglio['cons_euro'] : null);
if ($cons === null) {
return;
}
DB::table('contabilita_situazione_iniziale_conguagli')->insert([
'stabile_id' => (int) $stabileId,
'legacy_year' => $this->legacyYear,
'cod_tab' => $codTab,
'n_stra' => $nStra && $nStra > 0 ? $nStra : null,
'id_cond' => $idCond,
'cons_euro' => $cons,
'incluso' => 1,
'origine' => 'manuale',
'created_at' => now(),
'updated_at' => now(),
]);
$this->formConguaglio['id_cond'] = null;
$this->formConguaglio['cons_euro'] = null;
$this->reloadData();
}
}