3331 lines
131 KiB
PHP
3331 lines
131 KiB
PHP
<?php
|
|
namespace App\Filament\Pages\Gescon;
|
|
|
|
use App\Models\User;
|
|
use App\Models\UserSetting;
|
|
use App\Support\AnnoGestioneContext;
|
|
use App\Support\StabileContext;
|
|
use BackedEnum;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Livewire\WithPagination;
|
|
use UnitEnum;
|
|
|
|
class Ordinarie extends Page
|
|
{
|
|
use WithPagination;
|
|
|
|
protected static ?string $navigationLabel = 'Gest. ordinaria';
|
|
|
|
protected static ?string $title = 'Gestione ordinaria';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-calculator';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'GESCON';
|
|
|
|
protected static ?int $navigationSort = 20;
|
|
|
|
protected static ?string $slug = 'gescon/ordinarie';
|
|
|
|
protected string $view = 'filament.pages.gescon.section';
|
|
|
|
public string $sectionKey = 'ordinarie';
|
|
|
|
public int $perPage = 50;
|
|
|
|
public ?string $search = null;
|
|
public ?string $filterCodForn = null;
|
|
public ?string $filterCodSpe = null;
|
|
public ?string $filterTabella = null;
|
|
public ?string $filterBenef = null;
|
|
public bool $filterRdaOnly = false;
|
|
public ?int $filterAnno = null;
|
|
|
|
public string $viewTab = 'operazioni';
|
|
public string $sortField = 'id_operaz';
|
|
public string $sortDirection = 'desc';
|
|
public bool $netgesconWorkflowMode = false;
|
|
public bool $onlyStraordinarieMode = false;
|
|
public ?int $selectedStraordinaria = null;
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = Auth::user();
|
|
|
|
return $user instanceof User
|
|
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
|
|
}
|
|
|
|
public ?string $mastrinoTabella = null;
|
|
public ?string $mastrinoCodSpe = null;
|
|
public array $mastrinoRows = [];
|
|
|
|
public array $acquaRipartoRows = [];
|
|
|
|
public function mount(): void
|
|
{
|
|
$user = Auth::user();
|
|
if ($user instanceof User && $this->filterAnno === null) {
|
|
$this->filterAnno = AnnoGestioneContext::resolveActiveAnno($user);
|
|
}
|
|
|
|
$tab = request()->query('tab');
|
|
if (in_array($tab, ['operazioni', 'consuntivo', 'straordinarie', 'acqua', 'incassi', 'fatture'], true)) {
|
|
$this->viewTab = $tab;
|
|
}
|
|
|
|
if ($this->onlyStraordinarieMode) {
|
|
$straord = request()->query('straord');
|
|
if (is_numeric($straord)) {
|
|
$this->selectedStraordinaria = (int) $straord;
|
|
}
|
|
|
|
if (! in_array($this->viewTab, ['operazioni', 'consuntivo'], true)) {
|
|
$this->viewTab = 'operazioni';
|
|
}
|
|
}
|
|
|
|
if ($this->netgesconWorkflowMode) {
|
|
$this->sortField = 'dt_spe';
|
|
$this->sortDirection = 'asc';
|
|
}
|
|
}
|
|
|
|
public function getStraordinarieGestioniProperty(): array
|
|
{
|
|
if (! $this->onlyStraordinarieMode) {
|
|
return [];
|
|
}
|
|
|
|
if (! Schema::connection('gescon_import')->hasTable('operazioni')) {
|
|
return [];
|
|
}
|
|
|
|
$query = DB::connection('gescon_import')
|
|
->table('operazioni')
|
|
->where('gestione', 'S')
|
|
->whereNotNull('n_stra');
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) {
|
|
$query->where('cod_stabile', $legacyCode);
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$query->whereYear('dt_spe', $this->filterAnno);
|
|
}
|
|
|
|
$rows = $query
|
|
->selectRaw('n_stra, MIN(dt_spe) as data_inizio, MAX(dt_spe) as data_fine, COUNT(*) as totale')
|
|
->groupBy('n_stra')
|
|
->orderBy('n_stra')
|
|
->get();
|
|
|
|
$out = [];
|
|
foreach ($rows as $row) {
|
|
$nStra = is_numeric($row->n_stra ?? null) ? (int) $row->n_stra : null;
|
|
if (! $nStra) {
|
|
continue;
|
|
}
|
|
|
|
$periodo = null;
|
|
if (! empty($row->data_inizio) || ! empty($row->data_fine)) {
|
|
$start = ! empty($row->data_inizio) ? (string) \Carbon\Carbon::parse($row->data_inizio)->format('d/m/Y') : '—';
|
|
$end = ! empty($row->data_fine) ? (string) \Carbon\Carbon::parse($row->data_fine)->format('d/m/Y') : '—';
|
|
$periodo = $start . ' → ' . $end;
|
|
}
|
|
|
|
$out[] = [
|
|
'n_stra' => $nStra,
|
|
'label' => 'Gestione straordinaria #' . $nStra,
|
|
'periodo' => $periodo,
|
|
'totale' => (int) ($row->totale ?? 0),
|
|
];
|
|
}
|
|
|
|
if ($this->selectedStraordinaria === null && ! empty($out)) {
|
|
$this->selectedStraordinaria = (int) ($out[0]['n_stra'] ?? null);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
private function resolveLegacyCodiceStabile(): ?string
|
|
{
|
|
$user = Auth::user();
|
|
$stabile = StabileContext::getActiveStabile($user);
|
|
|
|
return StabileContext::preferredLegacyCode($stabile);
|
|
}
|
|
|
|
private function resolveLegacyYearForRiparto(?string $codStabile): ?string
|
|
{
|
|
if (! $codStabile) {
|
|
return null;
|
|
}
|
|
|
|
return (string) (DB::connection('gescon_import')
|
|
->table('dett_tab')
|
|
->where('cod_stabile', $codStabile)
|
|
->max('legacy_year') ?: '');
|
|
}
|
|
|
|
public function getLegacyOperazioniScopedByStabileProperty(): bool
|
|
{
|
|
return Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile');
|
|
}
|
|
|
|
public function getLegacyGestioneLabelProperty(): ?string
|
|
{
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if (! $legacyCode) {
|
|
return null;
|
|
}
|
|
|
|
$query = DB::connection('gescon_import')
|
|
->table('anni_gestione')
|
|
->where('anni_gestione.cod_stabile', $legacyCode);
|
|
|
|
if ($this->filterAnno) {
|
|
$query->where('anni_gestione.anno', (int) $this->filterAnno);
|
|
}
|
|
|
|
$row = $query
|
|
->leftJoin('stabili', 'stabili.cod_stabile', '=', 'anni_gestione.cod_stabile')
|
|
->orderByDesc('anni_gestione.anno')
|
|
->first([
|
|
'anni_gestione.anno as anno',
|
|
'stabili.codice_directory as codice_directory',
|
|
]);
|
|
if (! $row) {
|
|
return null;
|
|
}
|
|
|
|
$dir = trim((string) ($row->codice_directory ?? ''));
|
|
$anno = trim((string) ($row->anno ?? ''));
|
|
if ($dir === '' && $anno === '') {
|
|
return null;
|
|
}
|
|
|
|
$dirLabel = $dir !== '' ? $dir : '—';
|
|
$annoLabel = $anno !== '' ? $anno : '—';
|
|
|
|
return 'Gestione ' . $dirLabel . ' → ' . $annoLabel;
|
|
}
|
|
|
|
public function getLegacyGestioneAnnoProperty(): ?int
|
|
{
|
|
if ($this->filterAnno) {
|
|
return (int) $this->filterAnno;
|
|
}
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if (! $legacyCode) {
|
|
return null;
|
|
}
|
|
|
|
$row = DB::connection('gescon_import')
|
|
->table('anni_gestione')
|
|
->where('anni_gestione.cod_stabile', $legacyCode)
|
|
->orderByDesc('anni_gestione.anno')
|
|
->first(['anni_gestione.anno as anno']);
|
|
|
|
return $row && is_numeric($row->anno ?? null) ? (int) $row->anno : null;
|
|
}
|
|
|
|
public function getAcquaSummaryProperty(): array
|
|
{
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
$legacyYear = $this->resolveLegacyYearForRiparto($legacyCode);
|
|
|
|
$rowsQuery = DB::connection('gescon_import')
|
|
->table('operazioni')
|
|
->where('gestione', 'O')
|
|
->whereIn('cod_spe', ['AC1', 'AC2']);
|
|
|
|
if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) {
|
|
$rowsQuery->where('cod_stabile', $legacyCode);
|
|
}
|
|
|
|
$rows = $rowsQuery
|
|
->select('cod_spe')
|
|
->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale')
|
|
->groupBy('cod_spe')
|
|
->get();
|
|
|
|
$byCode = [];
|
|
foreach ($rows as $r) {
|
|
$byCode[(string) $r->cod_spe] = (float) ($r->totale ?? 0);
|
|
}
|
|
|
|
$riparto = DB::connection('gescon_import')
|
|
->table('dett_tab')
|
|
->where('cod_tab', 'ACQUA')
|
|
->when($legacyCode, fn($q) => $q->where('cod_stabile', $legacyCode))
|
|
->when($legacyYear, fn($q) => $q->where('legacy_year', $legacyYear))
|
|
->sum(DB::raw('COALESCE(cons_euro, 0)'));
|
|
|
|
$totaleFatture = array_sum($byCode);
|
|
|
|
return [
|
|
'voci' => $byCode,
|
|
'totale' => $totaleFatture,
|
|
'riparto_totale' => (float) $riparto,
|
|
'riparto_delta' => (float) ($riparto - $totaleFatture),
|
|
];
|
|
}
|
|
|
|
public function getAcquaRipartoProperty(): array
|
|
{
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
$legacyYear = $this->resolveLegacyYearForRiparto($legacyCode);
|
|
|
|
if (! $legacyCode) {
|
|
return ['rows' => [], 'totale' => 0.0];
|
|
}
|
|
|
|
$condQuery = DB::connection('gescon_import')
|
|
->table('condomin')
|
|
->where('cod_stabile', $legacyCode);
|
|
if ($legacyYear) {
|
|
$condQuery->where('legacy_year', $legacyYear);
|
|
}
|
|
$condRows = $condQuery
|
|
->get([
|
|
'id_cond',
|
|
'scala',
|
|
'interno',
|
|
'piano',
|
|
'cognome',
|
|
'nome',
|
|
'proprietario_denominazione',
|
|
'inquilino_denominazione',
|
|
'inquil_nome',
|
|
])
|
|
->keyBy('id_cond');
|
|
|
|
$ripartoQuery = DB::connection('gescon_import')
|
|
->table('dett_tab')
|
|
->where('cod_tab', 'ACQUA')
|
|
->where('cod_stabile', $legacyCode);
|
|
if ($legacyYear) {
|
|
$ripartoQuery->where('legacy_year', $legacyYear);
|
|
}
|
|
|
|
$ripartoRows = $ripartoQuery
|
|
->orderBy('id_cond')
|
|
->orderBy('cond_inquil')
|
|
->get(['id_cond', 'cond_inquil', 'cons_euro']);
|
|
|
|
$out = [];
|
|
$totale = 0.0;
|
|
foreach ($ripartoRows as $r) {
|
|
$cond = $condRows[$r->id_cond] ?? null;
|
|
$ruolo = strtoupper((string) ($r->cond_inquil ?? ''));
|
|
$nominativo = '';
|
|
if ($cond) {
|
|
if ($ruolo === 'I') {
|
|
$nominativo = trim((string) ($cond->inquilino_denominazione ?? ''));
|
|
if ($nominativo === '') {
|
|
$nominativo = trim((string) ($cond->inquil_nome ?? ''));
|
|
}
|
|
} else {
|
|
$nominativo = trim((string) ($cond->proprietario_denominazione ?? ''));
|
|
if ($nominativo === '') {
|
|
$nominativo = trim((string) (($cond->cognome ?? '') . ' ' . ($cond->nome ?? '')));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($ruolo === 'I' && $nominativo === '') {
|
|
continue;
|
|
}
|
|
|
|
if ($nominativo === '') {
|
|
$nominativo = '—';
|
|
}
|
|
|
|
$val = (float) ($r->cons_euro ?? 0);
|
|
$totale += $val;
|
|
$out[] = [
|
|
'id_cond' => $r->id_cond,
|
|
'ruolo' => $ruolo !== '' ? $ruolo : 'C',
|
|
'nominativo' => $nominativo,
|
|
'scala' => $cond->scala ?? null,
|
|
'interno' => $cond->interno ?? null,
|
|
'piano' => $cond->piano ?? null,
|
|
'cons_euro' => $val,
|
|
];
|
|
}
|
|
|
|
return ['rows' => $out, 'totale' => $totale];
|
|
}
|
|
|
|
public ?int $dettPersNSpe = null;
|
|
public array $dettPersRows = [];
|
|
public float $dettPersTotale = 0.0;
|
|
public float $dettPersImportoOperazione = 0.0;
|
|
public float $dettPersDelta = 0.0;
|
|
public bool $dettPersQuadraturaOk = true;
|
|
public string $dettPersRipartoMode = 'legacy';
|
|
public ?string $dettPersRipartoTabella = null;
|
|
public ?string $dettPersRipartoTarget = null;
|
|
public array $dettPersRipartoTabellaOptions = [];
|
|
public array $dettPersRipartoTargetOptions = [];
|
|
public array $dettPersRecipients = [];
|
|
public array $dettPersLegacyAmounts = [];
|
|
public ?string $dettPersLegacyCode = null;
|
|
public ?string $dettPersLegacyYear = null;
|
|
public array $dettPersLegacyUnico = [];
|
|
|
|
public function getOperazioniProperty()
|
|
{
|
|
$base = $this->buildOperazioniQuery();
|
|
|
|
$paginator = $this->applyOperazioniSorting($base)
|
|
->paginate($this->perPage);
|
|
|
|
$items = $paginator->getCollection();
|
|
$rdaByCode = [];
|
|
foreach ($items as $row) {
|
|
$rdaCode = null;
|
|
if (! empty($row->rif_rda)) {
|
|
$rdaCode = $row->rif_rda;
|
|
} elseif (is_string($row->benef ?? null) && str_starts_with(strtoupper(trim((string) $row->benef)), 'RDA:')) {
|
|
$rdaCode = trim(substr(trim((string) $row->benef), 4));
|
|
}
|
|
|
|
if ($rdaCode) {
|
|
$benef = trim((string) ($row->benef ?? ''));
|
|
$isRdaLine = $benef !== '' && str_starts_with(strtoupper($benef), 'RDA:');
|
|
if ($isRdaLine) {
|
|
$rdaByCode[$rdaCode] = [
|
|
'importo' => $this->computeSignedAmount($row),
|
|
'data' => $row->dt_spe ?? null,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
$codSpe = $items->pluck('cod_spe')->filter()->unique()->values()->all();
|
|
$codFor = $items
|
|
->pluck('cod_for')
|
|
->filter()
|
|
->map(fn($v) => trim((string) $v))
|
|
->filter()
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
$codForNoZero = array_values(array_filter(array_unique(array_map(fn($v) => ltrim((string) $v, '0'), $codFor))));
|
|
$codFor = array_values(array_unique(array_merge($codFor, $codForNoZero)));
|
|
|
|
$codTab = $items->pluck('tabella')->filter()->unique()->values()->all();
|
|
|
|
$vociMap = [];
|
|
$vociTabellaMap = [];
|
|
if (! empty($codSpe)) {
|
|
$vociRows = DB::connection('gescon_import')
|
|
->table('voc_spe')
|
|
->whereIn('cod', $codSpe)
|
|
->get(['cod', 'descriz', 'tabella']);
|
|
|
|
$vociMap = $vociRows
|
|
->mapWithKeys(fn($v) => [$v->cod => $v->descriz])
|
|
->all();
|
|
|
|
$vociTabellaMap = $vociRows
|
|
->mapWithKeys(fn($v) => [$v->cod => $v->tabella])
|
|
->all();
|
|
}
|
|
|
|
$tabFromVoci = array_values(array_filter(array_unique(array_map('strval', $vociTabellaMap))));
|
|
if (! empty($tabFromVoci)) {
|
|
$codTab = array_values(array_unique(array_merge($codTab, $tabFromVoci)));
|
|
}
|
|
|
|
$tabelleMap = [];
|
|
if (! empty($codTab)) {
|
|
$tabelleMap = DB::connection('gescon_import')
|
|
->table('tabelle_millesimali')
|
|
->whereIn('cod_tabella', $codTab)
|
|
->pluck('denominazione', 'cod_tabella')
|
|
->all();
|
|
}
|
|
|
|
$fornitoriMap = [];
|
|
$fornitoriIdMap = [];
|
|
$codFornToLegacyId = [];
|
|
$legacyIds = [];
|
|
if (! empty($codFor)) {
|
|
$useStg = Schema::hasTable('stg_fornitori_gescon');
|
|
$legacyRows = $useStg
|
|
? DB::table('stg_fornitori_gescon')
|
|
->whereIn('cod_forn', $codFor)
|
|
->get(['cod_forn', 'legacy_id_fornitore', 'denominazione', 'cognome', 'nome'])
|
|
: DB::connection('gescon_import')
|
|
->table('mdb_fornitori')
|
|
->whereIn('cod_forn', $codFor)
|
|
->get(['cod_forn', 'id_fornitore', 'denominazione', 'cognome', 'nome']);
|
|
|
|
$fornitoriMap = $legacyRows
|
|
->mapWithKeys(function ($f) {
|
|
$label = $f->denominazione ?: trim(($f->nome ?? '') . ' ' . ($f->cognome ?? ''));
|
|
return [trim((string) $f->cod_forn) => $label];
|
|
})
|
|
->all();
|
|
|
|
$codFornToLegacyId = $legacyRows
|
|
->mapWithKeys(fn($f) => [
|
|
trim((string) $f->cod_forn) => $useStg ? $f->cod_forn : ($f->id_fornitore ?? null),
|
|
])
|
|
->all();
|
|
|
|
foreach (array_keys($fornitoriMap) as $code) {
|
|
$trimmed = trim((string) $code);
|
|
$noZero = ltrim($trimmed, '0');
|
|
if ($trimmed !== '' && ! isset($fornitoriMap[$trimmed])) {
|
|
$fornitoriMap[$trimmed] = $fornitoriMap[$code];
|
|
}
|
|
if ($noZero !== '' && ! isset($fornitoriMap[$noZero])) {
|
|
$fornitoriMap[$noZero] = $fornitoriMap[$code];
|
|
}
|
|
if ($trimmed !== '' && ! isset($codFornToLegacyId[$trimmed]) && isset($codFornToLegacyId[$code])) {
|
|
$codFornToLegacyId[$trimmed] = $codFornToLegacyId[$code];
|
|
}
|
|
if ($noZero !== '' && ! isset($codFornToLegacyId[$noZero]) && isset($codFornToLegacyId[$code])) {
|
|
$codFornToLegacyId[$noZero] = $codFornToLegacyId[$code];
|
|
}
|
|
}
|
|
|
|
$legacyIds = $legacyRows
|
|
->map(fn($f) => $useStg ? $f->cod_forn : ($f->id_fornitore ?? null))
|
|
->filter()
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
$activeStabileId = null;
|
|
$authUser = Auth::user();
|
|
if ($authUser) {
|
|
$activeStabileId = StabileContext::resolveActiveStabileId($authUser);
|
|
}
|
|
|
|
if (! empty($legacyIds) && Schema::hasTable('fornitori') && $this->netgesconWorkflowMode) {
|
|
$activeAmministratoreId = null;
|
|
if ($activeStabileId && Schema::hasTable('stabili')) {
|
|
$activeAmministratoreId = DB::table('stabili')
|
|
->where('id', (int) $activeStabileId)
|
|
->value('amministratore_id');
|
|
}
|
|
|
|
$fornitoriRows = DB::table('fornitori')
|
|
->whereIn('old_id', $legacyIds)
|
|
->get(['id', 'old_id', 'amministratore_id']);
|
|
|
|
$fornitoriByOldId = $fornitoriRows
|
|
->groupBy('old_id')
|
|
->map(function ($rows) use ($activeAmministratoreId) {
|
|
if ($activeAmministratoreId) {
|
|
$match = $rows->first(fn($row) => (int) ($row->amministratore_id ?? 0) === (int) $activeAmministratoreId);
|
|
if ($match) {
|
|
return (int) $match->id;
|
|
}
|
|
}
|
|
|
|
return (int) ($rows->first()->id ?? 0);
|
|
})
|
|
->filter(fn($value) => $value > 0)
|
|
->all();
|
|
|
|
$fornitoriIdMap = $fornitoriByOldId;
|
|
} elseif (! empty($legacyIds)) {
|
|
$fornitoriIdMap = DB::table('fornitori')
|
|
->whereIn('old_id', $legacyIds)
|
|
->pluck('id', 'old_id')
|
|
->all();
|
|
}
|
|
|
|
$fattureByFornitore = [];
|
|
$fattureByFornitoreDocumento = [];
|
|
$fattureByDocumentoGlobale = [];
|
|
$fornitoriLocaliById = [];
|
|
$vociLocaliByCode = [];
|
|
$fornitoriIds = array_values(array_filter($fornitoriIdMap));
|
|
if (! empty($fornitoriIds) && Schema::hasTable('fornitori')) {
|
|
$fornitoriRows = DB::table('fornitori')
|
|
->whereIn('id', $fornitoriIds)
|
|
->get(['id', 'ragione_sociale', 'nome', 'cognome']);
|
|
|
|
foreach ($fornitoriRows as $fornitoreRow) {
|
|
$label = trim((string) ($fornitoreRow->ragione_sociale ?? ''));
|
|
if ($label === '') {
|
|
$label = trim((string) (($fornitoreRow->nome ?? '') . ' ' . ($fornitoreRow->cognome ?? '')));
|
|
}
|
|
$fornitoriLocaliById[(int) $fornitoreRow->id] = $label !== '' ? $label : null;
|
|
}
|
|
}
|
|
|
|
if ($activeStabileId && Schema::hasTable('voci_spesa')) {
|
|
$vociRows = DB::table('voci_spesa')
|
|
->where('stabile_id', (int) $activeStabileId)
|
|
->get(['codice', 'legacy_codice', 'descrizione', 'tabella_millesimale_default_id']);
|
|
|
|
$tabellaIds = $vociRows
|
|
->pluck('tabella_millesimale_default_id')
|
|
->filter(fn($value) => is_numeric($value))
|
|
->map(fn($value) => (int) $value)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$tabellaLocaleMap = [];
|
|
if (! empty($tabellaIds) && Schema::hasTable('tabelle_millesimali')) {
|
|
$tabellaLocaleMap = DB::table('tabelle_millesimali')
|
|
->whereIn('id', $tabellaIds)
|
|
->get(['id', 'codice_tabella', 'nome_tabella'])
|
|
->mapWithKeys(function ($tabellaRow) {
|
|
$name = trim((string) ($tabellaRow->nome_tabella ?? ''));
|
|
$code = trim((string) ($tabellaRow->codice_tabella ?? ''));
|
|
return [(int) $tabellaRow->id => ($name !== '' ? $name : $code)];
|
|
})
|
|
->all();
|
|
}
|
|
|
|
foreach ($vociRows as $voceRow) {
|
|
$codes = array_filter([
|
|
strtoupper(trim((string) ($voceRow->codice ?? ''))),
|
|
strtoupper(trim((string) ($voceRow->legacy_codice ?? ''))),
|
|
]);
|
|
|
|
$voceData = [
|
|
'descrizione' => trim((string) ($voceRow->descrizione ?? '')),
|
|
'tabella' => $tabellaLocaleMap[(int) ($voceRow->tabella_millesimale_default_id ?? 0)] ?? null,
|
|
];
|
|
|
|
foreach ($codes as $code) {
|
|
$vociLocaliByCode[$code] = $voceData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! empty($fornitoriIds) && DB::getSchemaBuilder()->hasTable('contabilita_fatture_fornitori')) {
|
|
$fatture = DB::table('contabilita_fatture_fornitori')
|
|
->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId))
|
|
->whereIn('fornitore_id', $fornitoriIds)
|
|
->select([
|
|
'id',
|
|
'fornitore_id',
|
|
'numero_documento',
|
|
'data_documento',
|
|
'totale',
|
|
'netto_da_pagare',
|
|
'data_pagamento',
|
|
'ritenuta_importo',
|
|
'data_versamento_ra',
|
|
'stato',
|
|
])
|
|
->get();
|
|
|
|
foreach ($fatture as $f) {
|
|
$fattureByFornitore[$f->fornitore_id][] = $f;
|
|
|
|
$docNumber = $this->normalizeInvoiceNumber($f->numero_documento ?? null);
|
|
$docDate = ! empty($f->data_documento) ? (string) \Carbon\Carbon::parse($f->data_documento)->format('Y-m-d') : null;
|
|
if ($docNumber !== null && $docDate !== null) {
|
|
$key = (int) $f->fornitore_id . '|' . $docNumber . '|' . $docDate;
|
|
$fattureByFornitoreDocumento[$key][] = $f;
|
|
|
|
$globalKey = $docNumber . '|' . $docDate;
|
|
$fattureByDocumentoGlobale[$globalKey][] = $f;
|
|
}
|
|
}
|
|
}
|
|
|
|
$mapped = $items->map(function ($row) use ($vociMap, $vociTabellaMap, $tabelleMap, $fornitoriMap, $fornitoriIdMap, $fattureByFornitore, $fattureByFornitoreDocumento, $fattureByDocumentoGlobale, $fornitoriLocaliById, $vociLocaliByCode, $codFornToLegacyId, $rdaByCode) {
|
|
$row->voce_descrizione = $vociMap[$row->cod_spe ?? ''] ?? null;
|
|
if (empty($row->tabella) && ! empty($vociTabellaMap[$row->cod_spe ?? ''])) {
|
|
$row->tabella = $vociTabellaMap[$row->cod_spe ?? ''];
|
|
}
|
|
$row->tabella_descrizione = $tabelleMap[$row->tabella ?? ''] ?? null;
|
|
$codFor = trim((string) ($row->cod_for ?? ''));
|
|
$codForNoZero = ltrim($codFor, '0');
|
|
$row->fornitore_nome = $fornitoriMap[$codFor] ?? ($codForNoZero !== '' ? ($fornitoriMap[$codForNoZero] ?? null) : null);
|
|
$row->fornitore_id = null;
|
|
if ($codFor !== '') {
|
|
$legacyId = $codFornToLegacyId[$codFor] ?? ($codForNoZero !== '' ? ($codFornToLegacyId[$codForNoZero] ?? null) : null);
|
|
if ($legacyId && isset($fornitoriIdMap[$legacyId])) {
|
|
$row->fornitore_id = (int) $fornitoriIdMap[$legacyId];
|
|
}
|
|
}
|
|
|
|
$row->fornitore_locale_nome = null;
|
|
$row->voce_spesa_locale = null;
|
|
$row->tabella_locale = null;
|
|
if ($this->netgesconWorkflowMode) {
|
|
$row->fornitore_locale_nome = $row->fornitore_id ? ($fornitoriLocaliById[$row->fornitore_id] ?? null) : null;
|
|
|
|
$voceLocale = $vociLocaliByCode[strtoupper((string) ($row->cod_spe ?? ''))] ?? null;
|
|
$row->voce_spesa_locale = $voceLocale['descrizione'] ?? null;
|
|
$row->tabella_locale = $voceLocale['tabella'] ?? null;
|
|
}
|
|
|
|
$row->rda_code = null;
|
|
$row->is_rda_line = false;
|
|
if (! empty($row->rif_rda)) {
|
|
$row->rda_code = $row->rif_rda;
|
|
} elseif (is_string($row->benef ?? null) && str_starts_with(strtoupper(trim((string) $row->benef)), 'RDA:')) {
|
|
$row->rda_code = trim(substr(trim((string) $row->benef), 4));
|
|
$row->is_rda_line = true;
|
|
}
|
|
|
|
$row->ra_importo = $row->netto_vers_rda ?? $row->importo_euro_770 ?? null;
|
|
$row->ra_data = null;
|
|
$row->fe_numero = null;
|
|
$row->fe_totale = null;
|
|
$row->fe_fattura_id = null;
|
|
$row->fe_ricevuta_id = null;
|
|
$row->fe_display = $row->fe_uid ?? null;
|
|
|
|
if ($row->ra_importo !== null) {
|
|
$raw = (string) $row->ra_importo;
|
|
$raw = str_replace(['.', ' '], ['', ''], $raw);
|
|
$raw = str_replace(',', '.', $raw);
|
|
$row->ra_importo = is_numeric($raw) ? (float) $raw : null;
|
|
}
|
|
|
|
$amount = (float) ($row->importo_euro ?? $row->importo ?? 0);
|
|
$row->importo_signed = $this->computeSignedAmount($row);
|
|
$row->rda_row_importo = null;
|
|
$row->rda_row_data = null;
|
|
if (! $row->is_rda_line && ! empty($row->rda_code) && isset($rdaByCode[$row->rda_code])) {
|
|
$row->rda_row_importo = $rdaByCode[$row->rda_code]['importo'] ?? null;
|
|
$row->rda_row_data = $rdaByCode[$row->rda_code]['data'] ?? null;
|
|
}
|
|
$fornId = $row->fornitore_id;
|
|
$best = null;
|
|
$docNumber = $this->normalizeInvoiceNumber($row->num_fat ?? null);
|
|
$docDate = ! empty($row->dt_fat) ? (string) \Carbon\Carbon::parse($row->dt_fat)->format('Y-m-d') : null;
|
|
if ($this->netgesconWorkflowMode && $fornId && $docNumber !== null && $docDate !== null) {
|
|
$strictKey = $fornId . '|' . $docNumber . '|' . $docDate;
|
|
if (! empty($fattureByFornitoreDocumento[$strictKey])) {
|
|
$best = $fattureByFornitoreDocumento[$strictKey][0];
|
|
}
|
|
}
|
|
|
|
if ($this->netgesconWorkflowMode && ! $best && $docNumber !== null && $docDate !== null) {
|
|
$globalKey = $docNumber . '|' . $docDate;
|
|
$globalMatches = $fattureByDocumentoGlobale[$globalKey] ?? [];
|
|
if (count($globalMatches) === 1) {
|
|
$best = $globalMatches[0];
|
|
}
|
|
}
|
|
|
|
if (! $best && $fornId && isset($fattureByFornitore[$fornId])) {
|
|
foreach ($fattureByFornitore[$fornId] as $f) {
|
|
$tot = (float) ($f->totale ?? 0);
|
|
$net = (float) ($f->netto_da_pagare ?? 0);
|
|
$matchTot = abs(abs($amount) - $tot) <= 0.01;
|
|
$matchNet = abs(abs($amount) - $net) <= 0.01;
|
|
if ($matchTot || $matchNet) {
|
|
$best = $f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($best) {
|
|
$row->fe_fattura_id = $best->id ?? null;
|
|
$row->fe_ricevuta_id = $best->fattura_elettronica_id ?? null;
|
|
$row->fe_numero = $best->numero_documento ?? null;
|
|
$row->fe_totale = $best->totale ?? $best->netto_da_pagare ?? null;
|
|
$row->fe_data = $best->data_documento ?? null;
|
|
$row->fe_netto = $best->netto_da_pagare ?? null;
|
|
$row->fe_stato = $best->stato ?? null;
|
|
$row->bonifico_data = $best->data_pagamento ?? null;
|
|
$row->ra_importo = $row->ra_importo ?? ($best->ritenuta_importo ?? null);
|
|
$row->ra_data = $best->data_versamento_ra ?? null;
|
|
}
|
|
|
|
if ($this->netgesconWorkflowMode && empty($row->bonifico_data) && ! empty($row->dt_spe)) {
|
|
$row->bonifico_data = $row->dt_spe;
|
|
}
|
|
|
|
$row->rda_importo_calcolata = null;
|
|
$row->rda_scadenza = null;
|
|
$row->rda_stato = null;
|
|
$row->protocollo_logico = null;
|
|
if ($this->netgesconWorkflowMode) {
|
|
$row->rda_importo_calcolata = $row->rda_row_importo ?? $row->ra_importo ?? null;
|
|
$row->rda_scadenza = $this->computeRdaDueDate($row->bonifico_data ?? null);
|
|
if (! empty($row->rda_importo_calcolata) && $row->rda_scadenza) {
|
|
if (! empty($row->rda_row_data)) {
|
|
$row->rda_stato = (\Carbon\Carbon::parse($row->rda_row_data)->lte(\Carbon\Carbon::parse($row->rda_scadenza)))
|
|
? 'versata in termine'
|
|
: 'versata in ritardo';
|
|
} else {
|
|
$row->rda_stato = 'da versare';
|
|
}
|
|
}
|
|
|
|
$row->protocollo_logico = 'debito';
|
|
if (! empty($row->rda_importo_calcolata)) {
|
|
$row->protocollo_logico = 'bonifico+rda';
|
|
}
|
|
}
|
|
|
|
if (! empty($row->rda_code) && empty($row->fe_display) && ! empty($row->ra_importo)) {
|
|
$row->fe_display = 'RA € ' . number_format((float) $row->ra_importo, 2, ',', '.');
|
|
}
|
|
|
|
if (! empty($row->rda_code) && $row->fe_fattura_id && ! empty($row->dt_spe) && ! empty($row->importo_spese) && empty($row->ra_data)) {
|
|
try {
|
|
$dt = \Carbon\Carbon::parse($row->dt_spe)->format('Y-m-d');
|
|
DB::table('contabilita_fatture_fornitori')
|
|
->where('id', $row->fe_fattura_id)
|
|
->update([
|
|
'data_versamento_ra' => $dt,
|
|
'ritenuta_importo' => $row->ra_importo,
|
|
'updated_at' => now(),
|
|
]);
|
|
$row->ra_data = $dt;
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) {
|
|
$query->where('cod_stabile', $legacyCode);
|
|
}
|
|
$row->tipo_movimento = null;
|
|
if (! empty($row->importo_spese)) {
|
|
$row->tipo_movimento = 'Spesa';
|
|
} elseif (! empty($row->importo_entrate)) {
|
|
$row->tipo_movimento = 'Entrata';
|
|
} elseif (! empty($row->importo_debiti)) {
|
|
$row->tipo_movimento = 'Debito';
|
|
} elseif (! empty($row->importo_crediti)) {
|
|
$row->tipo_movimento = 'Credito';
|
|
}
|
|
return $row;
|
|
});
|
|
|
|
$paginator->setCollection(
|
|
$mapped->filter(fn($row) => empty($row->is_rda_line))->values()
|
|
);
|
|
|
|
$indivRows = $items->filter(fn($r) => in_array(strtoupper((string) ($r->cod_spe ?? '')), ['I05', 'I06'], true) && ! empty($r->n_spe));
|
|
if ($indivRows->isNotEmpty()) {
|
|
$nSpeList = $indivRows->pluck('n_spe')->filter()->unique()->values()->all();
|
|
$dettMap = DB::connection('gescon_import')
|
|
->table('dett_pers')
|
|
->whereIn('n_spe', $nSpeList)
|
|
->select('n_spe')
|
|
->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale')
|
|
->groupBy('n_spe')
|
|
->pluck('totale', 'n_spe')
|
|
->all();
|
|
|
|
$paginator->setCollection(
|
|
$paginator->getCollection()->map(function ($row) use ($dettMap) {
|
|
if (in_array(strtoupper((string) ($row->cod_spe ?? '')), ['I05', 'I06'], true) && ! empty($row->n_spe)) {
|
|
$detTot = $this->normalizeMoney($dettMap[$row->n_spe] ?? 0);
|
|
$imp = $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0);
|
|
$row->dett_pers_totale = $detTot;
|
|
$row->dett_pers_delta = $imp - $detTot;
|
|
$row->dett_pers_ok = abs($row->dett_pers_delta) <= 0.01;
|
|
}
|
|
return $row;
|
|
})
|
|
);
|
|
}
|
|
|
|
return $paginator;
|
|
}
|
|
|
|
public function getConsuntivoProperty(): array
|
|
{
|
|
$base = $this->buildOperazioniQuery();
|
|
$excludeCods = ['Z01', 'Z02', 'I05'];
|
|
if (DB::connection('gescon_import')->getSchemaBuilder()->hasTable('cre_deb_preced')) {
|
|
$creDebCodes = DB::connection('gescon_import')
|
|
->table('cre_deb_preced')
|
|
->pluck('cod_voc')
|
|
->filter()
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
if (! empty($creDebCodes)) {
|
|
$excludeCods = array_values(array_unique(array_merge($excludeCods, $creDebCodes)));
|
|
}
|
|
}
|
|
$rows = $base
|
|
->whereNotIn('cod_spe', $excludeCods)
|
|
->select('tabella', 'cod_spe')
|
|
->selectRaw("SUM(
|
|
CASE
|
|
WHEN (COALESCE(importo_spese, 0) + COALESCE(importo_entrate, 0) + COALESCE(importo_debiti, 0) + COALESCE(importo_crediti, 0)) = 0 THEN
|
|
CASE
|
|
WHEN cod_spe = 'RB1' THEN -1 * COALESCE(importo_euro, importo, 0)
|
|
WHEN natura2 IN ('E', 'ENTRATA', 'ENTRATE', 'CREDITO') THEN -1 * COALESCE(importo_euro, importo, 0)
|
|
ELSE COALESCE(importo_euro, importo, 0)
|
|
END
|
|
ELSE (COALESCE(importo_spese, 0) + COALESCE(importo_debiti, 0) - COALESCE(importo_entrate, 0) - COALESCE(importo_crediti, 0))
|
|
END
|
|
) as totale")
|
|
->groupBy('tabella', 'cod_spe')
|
|
->get();
|
|
|
|
$frazRows = collect();
|
|
if (Schema::connection('gescon_import')->hasTable('fraz_dett') && Schema::connection('gescon_import')->hasTable('fraz_gen')) {
|
|
$parteCol = null;
|
|
if (Schema::connection('gescon_import')->hasColumn('fraz_dett', 'parte')) {
|
|
$parteCol = 'parte';
|
|
} elseif (Schema::connection('gescon_import')->hasColumn('fraz_dett', 'spe_parte')) {
|
|
$parteCol = 'spe_parte';
|
|
}
|
|
|
|
$frazQuery = DB::connection('gescon_import')
|
|
->table('fraz_dett as fd')
|
|
->join('fraz_gen as fg', 'fg.Progressivo', '=', 'fd.rif_progressivo')
|
|
->select([
|
|
'fg.Progressivo as progressivo',
|
|
'fg.cod_voce as cod_voce_gen',
|
|
'fg.Importo_voce as importo_voce',
|
|
'fd.cod_voce as cod_voce_det',
|
|
'fd.Importo_parte as importo_parte',
|
|
]);
|
|
|
|
if ($parteCol) {
|
|
$frazQuery->addSelect("fd.$parteCol as parte");
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$frazQuery->where('fg.legacy_year', (string) $this->filterAnno);
|
|
}
|
|
|
|
$frazItems = $frazQuery->get();
|
|
|
|
if ($frazItems->isNotEmpty()) {
|
|
$sumParteByProg = [];
|
|
if ($parteCol) {
|
|
foreach ($frazItems as $r) {
|
|
$prog = (string) ($r->progressivo ?? '');
|
|
if ($prog === '') {
|
|
continue;
|
|
}
|
|
$parteVal = is_numeric($r->parte ?? null) ? (float) $r->parte : 0.0;
|
|
$sumParteByProg[$prog] = ($sumParteByProg[$prog] ?? 0.0) + $parteVal;
|
|
}
|
|
}
|
|
|
|
$totByVoce = [];
|
|
foreach ($frazItems as $r) {
|
|
$codVoceDet = trim((string) ($r->cod_voce_det ?? ''));
|
|
$codVoceGen = trim((string) ($r->cod_voce_gen ?? ''));
|
|
$codVoce = $codVoceDet !== '' ? $codVoceDet : $codVoceGen;
|
|
if ($codVoce === '') {
|
|
continue;
|
|
}
|
|
|
|
$impParte = is_numeric($r->importo_parte ?? null) ? (float) $r->importo_parte : 0.0;
|
|
$impVoce = is_numeric($r->importo_voce ?? null) ? (float) $r->importo_voce : 0.0;
|
|
|
|
if (abs($impParte) < 0.00001 && $parteCol) {
|
|
$prog = (string) ($r->progressivo ?? '');
|
|
$base = $sumParteByProg[$prog] ?? 0.0;
|
|
$parteVal = is_numeric($r->parte ?? null) ? (float) $r->parte : 0.0;
|
|
if ($base > 0 && $impVoce != 0.0) {
|
|
$impParte = $impVoce * ($parteVal / $base);
|
|
}
|
|
}
|
|
|
|
if (abs($impParte) < 0.00001) {
|
|
continue;
|
|
}
|
|
|
|
$totByVoce[$codVoce] = ($totByVoce[$codVoce] ?? 0.0) + $impParte;
|
|
}
|
|
|
|
$frazRows = collect($totByVoce)->map(function ($totale, $codVoce) {
|
|
return (object) [
|
|
'tabella' => null,
|
|
'cod_spe' => $codVoce,
|
|
'totale' => $totale,
|
|
'origin' => 'fraz',
|
|
];
|
|
})->values();
|
|
}
|
|
}
|
|
|
|
$i05Operazioni = DB::connection('gescon_import')
|
|
->table('operazioni')
|
|
->where('cod_spe', 'I05')
|
|
->when(
|
|
$this->resolveLegacyCodiceStabile() && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'),
|
|
fn($q) => $q->where('cod_stabile', $this->resolveLegacyCodiceStabile())
|
|
)
|
|
->when($this->filterAnno, fn($q) => $q->whereYear('dt_spe', $this->filterAnno))
|
|
->get(['n_spe', 'importo_euro', 'importo']);
|
|
|
|
$nSpeI05 = $i05Operazioni
|
|
->pluck('n_spe')
|
|
->filter(fn($value) => is_numeric($value))
|
|
->map(fn($value) => (int) $value)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$dettByNSpe = [];
|
|
if (! empty($nSpeI05) && Schema::connection('gescon_import')->hasTable('dett_pers')) {
|
|
$dettByNSpe = DB::connection('gescon_import')
|
|
->table('dett_pers')
|
|
->whereIn('n_spe', $nSpeI05)
|
|
->select('n_spe')
|
|
->selectRaw('SUM(COALESCE(importo_euro, importo, 0)) as totale')
|
|
->groupBy('n_spe')
|
|
->pluck('totale', 'n_spe')
|
|
->all();
|
|
}
|
|
|
|
$i05 = $i05Operazioni->sum(function ($row) use ($dettByNSpe) {
|
|
$nSpe = is_numeric($row->n_spe ?? null) ? (int) $row->n_spe : null;
|
|
if ($nSpe !== null && array_key_exists($nSpe, $dettByNSpe)) {
|
|
return $this->normalizeMoney($dettByNSpe[$nSpe]);
|
|
}
|
|
|
|
return $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0);
|
|
});
|
|
|
|
$allRows = $rows
|
|
->map(function ($r) {
|
|
$r->origin = 'base';
|
|
return $r;
|
|
})
|
|
->concat($frazRows)
|
|
->when($i05 !== null, function ($c) use ($i05) {
|
|
return $c->push((object) [
|
|
'tabella' => 'INDIV',
|
|
'cod_spe' => 'I05',
|
|
'totale' => $i05,
|
|
'origin' => 'i05',
|
|
]);
|
|
});
|
|
|
|
$codes = $allRows->pluck('cod_spe')->filter()->unique()->values()->all();
|
|
$vociMap = [];
|
|
$tabellaByCod = [];
|
|
if (! empty($codes)) {
|
|
$vocRows = DB::connection('gescon_import')
|
|
->table('voc_spe')
|
|
->whereIn('cod', $codes)
|
|
->get(['cod', 'descriz', 'tabella']);
|
|
$vociMap = $vocRows->mapWithKeys(fn($v) => [$v->cod => $v->descriz])->all();
|
|
$tabellaByCod = $vocRows->mapWithKeys(fn($v) => [$v->cod => $v->tabella])->all();
|
|
}
|
|
|
|
$tabCodes = $allRows->map(function ($r) use ($tabellaByCod) {
|
|
return $r->tabella ?: ($tabellaByCod[$r->cod_spe ?? ''] ?? null);
|
|
})->filter()->unique()->values()->all();
|
|
|
|
$tabellaMap = [];
|
|
$tabellaInfo = [];
|
|
if (! empty($tabCodes)) {
|
|
$tabellaMap = DB::connection('gescon_import')
|
|
->table('tabelle_millesimali')
|
|
->whereIn('cod_tabella', $tabCodes)
|
|
->pluck('denominazione', 'cod_tabella')
|
|
->all();
|
|
|
|
$tabellaInfo = DB::connection('gescon_import')
|
|
->table('tabelle_millesimali')
|
|
->whereIn('cod_tabella', $tabCodes)
|
|
->get(['cod_tabella', 'denominazione', 'descrizione', 'tipologia', 'nord'])
|
|
->keyBy('cod_tabella')
|
|
->all();
|
|
}
|
|
|
|
$rows = $allRows->map(function ($r) use ($vociMap, $tabellaByCod, $tabellaMap, $tabellaInfo) {
|
|
$tabella = $r->tabella ?: ($tabellaByCod[$r->cod_spe ?? ''] ?? null);
|
|
$forcedHeader = null;
|
|
$codSpe = strtoupper((string) ($r->cod_spe ?? ''));
|
|
if (in_array($codSpe, ['I05', 'I06'], true)) {
|
|
$tabella = 'INDIV.';
|
|
$forcedHeader = 'INDIV. ADDEBITI INDIVIDUALI';
|
|
}
|
|
|
|
$info = $tabella && isset($tabellaInfo[$tabella]) ? $tabellaInfo[$tabella] : null;
|
|
$denom = $info?->denominazione ?? ($tabellaMap[$tabella ?? ''] ?? null);
|
|
$descr = $info?->descrizione ?? null;
|
|
$header = trim(($tabella ?? '—') . ' ' . ($denom ?? $descr ?? ''));
|
|
if ($denom && $descr && $denom !== $descr) {
|
|
$header = trim(($tabella ?? '—') . ' ' . $denom . ' (' . $descr . ')');
|
|
}
|
|
if ($forcedHeader) {
|
|
$header = $forcedHeader;
|
|
}
|
|
|
|
return [
|
|
'tabella' => $tabella ?? '—',
|
|
'tabella_descrizione' => $tabellaMap[$tabella ?? ''] ?? null,
|
|
'tabella_header' => $header,
|
|
'tabella_tipologia' => $info?->tipologia ?? null,
|
|
'tabella_nord' => is_numeric($info?->nord ?? null) ? (int) $info->nord : 9999,
|
|
'cod_spe' => $r->cod_spe ?? '—',
|
|
'voce' => $vociMap[$r->cod_spe ?? ''] ?? null,
|
|
'totale' => (float) ($r->totale ?? 0),
|
|
];
|
|
})
|
|
->filter(function (array $r): bool {
|
|
$tot = (float) ($r['totale'] ?? 0);
|
|
if ($tot !== 0.0) {
|
|
return true;
|
|
}
|
|
|
|
$tab = trim((string) ($r['tabella'] ?? ''));
|
|
$voce = trim((string) ($r['voce'] ?? ''));
|
|
if ($tab === '—') {
|
|
$tab = '';
|
|
}
|
|
if ($voce === '—') {
|
|
$voce = '';
|
|
}
|
|
return $tab !== '' || $voce !== '';
|
|
})
|
|
->values();
|
|
|
|
$rows = $rows->sortBy(function ($r) {
|
|
return sprintf('%05d|%s|%s', (int) ($r['tabella_nord'] ?? 9999), $r['tabella'] ?? '', $r['cod_spe'] ?? '');
|
|
})->values();
|
|
|
|
$groups = [
|
|
'ordinarie' => [],
|
|
'straordinarie' => [],
|
|
'acqua' => [],
|
|
];
|
|
|
|
foreach ($rows as $r) {
|
|
$tab = (string) ($r['tabella'] ?? '');
|
|
$tip = strtoupper((string) ($r['tabella_tipologia'] ?? ''));
|
|
$groupKey = 'ordinarie';
|
|
|
|
if ($tab === 'ACQUA' || str_starts_with($tab, 'AC')) {
|
|
$groupKey = 'acqua';
|
|
} elseif (in_array($tip, ['S', 'R'], true)) {
|
|
$groupKey = 'straordinarie';
|
|
}
|
|
|
|
if (! isset($groups[$groupKey][$tab])) {
|
|
$groups[$groupKey][$tab] = [
|
|
'header' => $r['tabella_header'] ?? $tab,
|
|
'nord' => $r['tabella_nord'] ?? 9999,
|
|
'rows' => [],
|
|
'subtotal' => 0.0,
|
|
];
|
|
}
|
|
|
|
$groups[$groupKey][$tab]['rows'][] = $r;
|
|
$groups[$groupKey][$tab]['subtotal'] += (float) ($r['totale'] ?? 0);
|
|
}
|
|
|
|
foreach ($groups as $k => $tabs) {
|
|
uasort($tabs, function ($a, $b) {
|
|
return ($a['nord'] ?? 9999) <=> ($b['nord'] ?? 9999);
|
|
});
|
|
$groups[$k] = $tabs;
|
|
}
|
|
|
|
return $groups;
|
|
}
|
|
|
|
public function getIncassiProperty()
|
|
{
|
|
if (! Schema::connection('gescon_import')->hasTable('incassi')) {
|
|
return null;
|
|
}
|
|
|
|
$q = DB::connection('gescon_import')->table('incassi');
|
|
|
|
$hasCondomin = Schema::connection('gescon_import')->hasTable('condomin');
|
|
if ($hasCondomin) {
|
|
$hasIncassiCodStabile = Schema::connection('gescon_import')->hasColumn('incassi', 'cod_stabile');
|
|
$hasCondominCodStabile = Schema::connection('gescon_import')->hasColumn('condomin', 'cod_stabile');
|
|
$hasCondominLegacyYear = Schema::connection('gescon_import')->hasColumn('condomin', 'legacy_year');
|
|
|
|
$q->leftJoin('condomin', function ($join) use ($hasIncassiCodStabile, $hasCondominCodStabile, $hasCondominLegacyYear): void {
|
|
$join->on('condomin.cod_cond', '=', 'incassi.cod_cond');
|
|
if ($hasIncassiCodStabile && $hasCondominCodStabile) {
|
|
$join->on('condomin.cod_stabile', '=', 'incassi.cod_stabile');
|
|
}
|
|
if ($hasCondominLegacyYear) {
|
|
$join->where('condomin.legacy_year', '=', '0001');
|
|
}
|
|
});
|
|
}
|
|
|
|
$this->applyIncassiFilters($q);
|
|
|
|
$codStabile = $this->resolveLegacyCodiceStabile();
|
|
|
|
$orderCol = Schema::connection('gescon_import')->hasColumn('incassi', 'dt_empag') ? 'dt_empag' : 'id';
|
|
|
|
$paginator = $q
|
|
->select([
|
|
'incassi.*',
|
|
'condomin.scala as cond_scala',
|
|
'condomin.interno as cond_interno',
|
|
'condomin.cognome as cond_cognome',
|
|
'condomin.nome as cond_nome',
|
|
'condomin.proprietario_denominazione as cond_proprietario_denominazione',
|
|
'condomin.inquilino_denominazione as cond_inquilino_denominazione',
|
|
'condomin.inquil_nome as cond_inquil_nome',
|
|
])
|
|
->orderByDesc($orderCol)
|
|
->orderByDesc('id')
|
|
->paginate($this->perPage);
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId(Auth::user());
|
|
$personaByCond = [];
|
|
$rateRowsByCond = [];
|
|
$codConds = $paginator->getCollection()
|
|
->pluck('cod_cond')
|
|
->filter(fn($v) => is_numeric($v))
|
|
->map(fn($v) => (int) $v)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
if (! empty($codConds) && Schema::connection('gescon_import')->hasTable('rate')) {
|
|
$rateQuery = DB::connection('gescon_import')
|
|
->table('rate')
|
|
->whereIn('cod_cond', $codConds);
|
|
|
|
if ($codStabile && Schema::connection('gescon_import')->hasColumn('rate', 'cod_stabile')) {
|
|
$rateQuery->where('cod_stabile', $codStabile);
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$year = (int) $this->filterAnno;
|
|
$rateQuery->where(function ($q) use ($year): void {
|
|
if (Schema::connection('gescon_import')->hasColumn('rate', 'data_emissione')) {
|
|
$q->orWhereYear('data_emissione', $year);
|
|
}
|
|
if (Schema::connection('gescon_import')->hasColumn('rate', 'data_scadenza')) {
|
|
$q->orWhereYear('data_scadenza', $year);
|
|
}
|
|
if (Schema::connection('gescon_import')->hasColumn('rate', 'data_pagamento')) {
|
|
$q->orWhereYear('data_pagamento', $year);
|
|
}
|
|
});
|
|
}
|
|
|
|
$rateRows = $rateQuery->get([
|
|
'cod_cond',
|
|
'n_emissione',
|
|
'data_emissione',
|
|
'data_scadenza',
|
|
'importo',
|
|
'importo_euro',
|
|
'causale',
|
|
'tipo_rata',
|
|
'pagata',
|
|
'data_pagamento',
|
|
]);
|
|
|
|
$rateRowsByCond = $rateRows
|
|
->groupBy(fn($r) => (int) ($r->cod_cond ?? 0))
|
|
->map(fn($rows) => $rows->values()->all())
|
|
->all();
|
|
}
|
|
|
|
if ($activeStabileId && Schema::hasTable('unita_immobiliari') && Schema::hasTable('persone_unita_relazioni') && Schema::hasTable('persone')) {
|
|
if (! empty($codConds) && Schema::hasColumn('unita_immobiliari', 'legacy_cond_id')) {
|
|
$unitaRows = DB::table('unita_immobiliari')
|
|
->where('stabile_id', (int) $activeStabileId)
|
|
->whereIn('legacy_cond_id', $codConds)
|
|
->get(['id', 'legacy_cond_id']);
|
|
|
|
$unitaIds = $unitaRows->pluck('id')->map(fn($v) => (int) $v)->all();
|
|
$legacyByUnita = $unitaRows->mapWithKeys(fn($r) => [(int) $r->id => (int) $r->legacy_cond_id])->all();
|
|
|
|
if (! empty($unitaIds)) {
|
|
$relRows = DB::table('persone_unita_relazioni as pur')
|
|
->join('persone as p', 'p.id', '=', 'pur.persona_id')
|
|
->whereIn('pur.unita_id', $unitaIds)
|
|
->where('pur.attivo', true)
|
|
->where(function ($q): void {
|
|
$q->whereNull('pur.data_fine')->orWhereDate('pur.data_fine', '>=', now()->toDateString());
|
|
})
|
|
->select(['pur.persona_id', 'pur.unita_id', 'pur.tipo_relazione', 'pur.data_inizio'])
|
|
->orderByDesc('pur.data_inizio')
|
|
->orderByDesc('pur.id')
|
|
->get();
|
|
|
|
foreach ($relRows as $rel) {
|
|
$unitaId = (int) ($rel->unita_id ?? 0);
|
|
$codCond = (int) ($legacyByUnita[$unitaId] ?? 0);
|
|
if ($codCond <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$tipo = strtolower(trim((string) ($rel->tipo_relazione ?? '')));
|
|
$pid = (int) ($rel->persona_id ?? 0);
|
|
if ($pid <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$roleClass = 'C';
|
|
if (in_array($tipo, ['inquilino', 'conduttore', 'affittuario', 'locatario'], true)) {
|
|
$roleClass = 'I';
|
|
}
|
|
|
|
$score = $this->relationScoreForIncasso($tipo, $roleClass);
|
|
$current = $personaByCond[$codCond][$roleClass] ?? null;
|
|
if (! $current || $score > ($current['score'] ?? -1)) {
|
|
$personaByCond[$codCond][$roleClass] = [
|
|
'persona_id' => $pid,
|
|
'score' => $score,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$paginator->setCollection(
|
|
$paginator->getCollection()->map(function ($row) use ($personaByCond, $rateRowsByCond) {
|
|
$tipo = strtoupper(trim((string) ($row->cond_inq ?? $row->cond_inquil ?? '')));
|
|
$nominativo = '';
|
|
if ($tipo === 'I') {
|
|
$nominativo = trim((string) ($row->cond_inquilino_denominazione ?? ''));
|
|
if ($nominativo === '') {
|
|
$nominativo = trim((string) ($row->cond_inquil_nome ?? ''));
|
|
}
|
|
} else {
|
|
$nominativo = trim((string) ($row->cond_proprietario_denominazione ?? ''));
|
|
if ($nominativo === '') {
|
|
$nominativo = trim((string) (($row->cond_cognome ?? '') . ' ' . ($row->cond_nome ?? '')));
|
|
}
|
|
}
|
|
|
|
$row->nominativo_pagante = $nominativo !== '' ? $nominativo : '—';
|
|
$row->scala_interno_short = $this->formatScalaInternoShort($row->cond_scala ?? null, $row->cond_interno ?? null);
|
|
|
|
$row->data_visuale = $row->dt_empag ?? $row->data_incasso ?? null;
|
|
$row->riferimento_visuale = $row->riferimento_pagamento ?? $row->n_riferimento ?? $row->n_rata_riferimento ?? $row->n_ricevuta ?? $row->n_mese ?? null;
|
|
|
|
$row->importo_visualizzato = $this->resolveIncassoImporto($row);
|
|
|
|
$codCond = is_numeric($row->cod_cond ?? null) ? (int) $row->cod_cond : 0;
|
|
$personaRef = null;
|
|
if ($codCond > 0) {
|
|
$personaRef = $personaByCond[$codCond][$tipo] ?? ($personaByCond[$codCond]['C'] ?? null);
|
|
}
|
|
$row->persona_id = $personaRef['persona_id'] ?? null;
|
|
$row->persona_url = $row->persona_id
|
|
? \App\Filament\Pages\Gescon\PersonaScheda::getUrl(['record' => (int) $row->persona_id], panel : 'admin-filament')
|
|
: null;
|
|
|
|
$row->rata_match = null;
|
|
$row->rata_match_source = null;
|
|
if ($codCond > 0 && ! empty($rateRowsByCond[$codCond] ?? [])) {
|
|
$match = $this->findRateMatchForIncasso($row, $rateRowsByCond[$codCond]);
|
|
if ($match) {
|
|
$row->rata_match = $match;
|
|
$row->rata_match_source = 'rate';
|
|
}
|
|
}
|
|
|
|
return $row;
|
|
})
|
|
);
|
|
|
|
return $paginator;
|
|
}
|
|
|
|
public function getIncassiRifSummaryProperty(): array
|
|
{
|
|
if (! Schema::connection('gescon_import')->hasTable('incassi')) {
|
|
return ['rows' => [], 'totale' => 0.0, 'totale_ccb' => 0.0, 'count' => 0];
|
|
}
|
|
|
|
$query = DB::connection('gescon_import')->table('incassi');
|
|
$this->applyIncassiFilters($query);
|
|
|
|
$select = ['id'];
|
|
foreach (['riferimento_pagamento', 'n_riferimento', 'n_rata_riferimento', 'n_ricevuta', 'n_mese', 'importo_euro', 'importo', 'importo_pagato_euro', 'dt_empag', 'data_incasso', 'cod_cassa'] as $col) {
|
|
if (Schema::connection('gescon_import')->hasColumn('incassi', $col)) {
|
|
$select[] = $col;
|
|
}
|
|
}
|
|
|
|
$rows = $query->get($select);
|
|
|
|
$summary = [];
|
|
$totale = 0.0;
|
|
$totaleCcb = 0.0;
|
|
foreach ($rows as $row) {
|
|
$rif = trim((string) ($row->riferimento_pagamento ?? $row->n_riferimento ?? $row->n_rata_riferimento ?? $row->n_ricevuta ?? $row->n_mese ?? ''));
|
|
if ($rif === '') {
|
|
$rif = '—';
|
|
}
|
|
|
|
$importo = $this->resolveIncassoImporto($row);
|
|
$totale += $importo;
|
|
$isCcb = strtoupper(trim((string) ($row->cod_cassa ?? ''))) === 'CCB';
|
|
if ($isCcb) {
|
|
$totaleCcb += $importo;
|
|
}
|
|
|
|
if (! isset($summary[$rif])) {
|
|
$summary[$rif] = [
|
|
'rif' => $rif,
|
|
'count' => 0,
|
|
'totale' => 0.0,
|
|
'totale_ccb' => 0.0,
|
|
'ultima_data' => null,
|
|
'casse' => [],
|
|
];
|
|
}
|
|
|
|
$summary[$rif]['count']++;
|
|
$summary[$rif]['totale'] += $importo;
|
|
if ($isCcb) {
|
|
$summary[$rif]['totale_ccb'] += $importo;
|
|
}
|
|
|
|
$cassa = trim((string) ($row->cod_cassa ?? ''));
|
|
if ($cassa !== '') {
|
|
$summary[$rif]['casse'][$cassa] = true;
|
|
}
|
|
|
|
$data = $row->dt_empag ?? $row->data_incasso ?? null;
|
|
if (! empty($data)) {
|
|
try {
|
|
$ymd = (string) \Carbon\Carbon::parse($data)->format('Y-m-d');
|
|
if (empty($summary[$rif]['ultima_data']) || $ymd > (string) $summary[$rif]['ultima_data']) {
|
|
$summary[$rif]['ultima_data'] = $ymd;
|
|
}
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
}
|
|
|
|
$rowsOut = collect($summary)
|
|
->map(function (array $row) {
|
|
$row['totale'] = round((float) ($row['totale'] ?? 0), 2);
|
|
$row['totale_ccb'] = round((float) ($row['totale_ccb'] ?? 0), 2);
|
|
$row['casse'] = implode(', ', array_keys($row['casse'] ?? []));
|
|
return $row;
|
|
})
|
|
->sortByDesc('totale')
|
|
->values()
|
|
->all();
|
|
|
|
return [
|
|
'rows' => $rowsOut,
|
|
'totale' => round($totale, 2),
|
|
'totale_ccb' => round($totaleCcb, 2),
|
|
'count' => (int) $rows->count(),
|
|
];
|
|
}
|
|
|
|
public function getRaMonthlyTributoSummaryProperty(): array
|
|
{
|
|
if (! Schema::hasTable('contabilita_fatture_fornitori')) {
|
|
return ['rows' => [], 'totale_fatta' => 0.0, 'totale_versata' => 0.0, 'totale_delta' => 0.0];
|
|
}
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId(Auth::user());
|
|
|
|
$query = DB::table('contabilita_fatture_fornitori')
|
|
->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId))
|
|
->whereRaw('COALESCE(ritenuta_importo, 0) <> 0');
|
|
|
|
if ($this->filterAnno) {
|
|
$year = (int) $this->filterAnno;
|
|
$query->where(function ($q) use ($year): void {
|
|
$q->whereYear('data_documento', $year)
|
|
->orWhereYear('data_versamento_ra', $year);
|
|
});
|
|
}
|
|
|
|
$rows = $query->get([
|
|
'fornitore_id',
|
|
'data_documento',
|
|
'data_versamento_ra',
|
|
'ritenuta_importo',
|
|
'ritenuta_codice_tributo',
|
|
]);
|
|
|
|
$fornitoreIds = $rows
|
|
->pluck('fornitore_id')
|
|
->filter(fn($v) => is_numeric($v))
|
|
->map(fn($v) => (int) $v)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$tributoByFornitore = $this->resolveTributoByFornitoreIdMap($fornitoreIds);
|
|
|
|
$summary = [];
|
|
$totFatta = 0.0;
|
|
$totVersata = 0.0;
|
|
|
|
foreach ($rows as $row) {
|
|
$importo = abs($this->normalizeMoney($row->ritenuta_importo ?? 0));
|
|
if ($importo <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$tributo = strtoupper(trim((string) ($row->ritenuta_codice_tributo ?? '')));
|
|
$fornitoreId = is_numeric($row->fornitore_id ?? null) ? (int) $row->fornitore_id : null;
|
|
if ($tributo === '' && $fornitoreId && ! empty($tributoByFornitore[$fornitoreId])) {
|
|
$tributo = (string) $tributoByFornitore[$fornitoreId];
|
|
}
|
|
if ($tributo === '') {
|
|
$tributo = '1040';
|
|
}
|
|
|
|
if (! empty($row->data_documento)) {
|
|
try {
|
|
$mese = (string) \Carbon\Carbon::parse($row->data_documento)->format('Y-m');
|
|
$key = $mese . '|' . $tributo;
|
|
if (! isset($summary[$key])) {
|
|
$summary[$key] = ['mese' => $mese, 'tributo' => $tributo, 'fatta' => 0.0, 'versata' => 0.0];
|
|
}
|
|
$summary[$key]['fatta'] += $importo;
|
|
$totFatta += $importo;
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
|
|
if (! empty($row->data_versamento_ra)) {
|
|
try {
|
|
$mese = (string) \Carbon\Carbon::parse($row->data_versamento_ra)->format('Y-m');
|
|
$key = $mese . '|' . $tributo;
|
|
if (! isset($summary[$key])) {
|
|
$summary[$key] = ['mese' => $mese, 'tributo' => $tributo, 'fatta' => 0.0, 'versata' => 0.0];
|
|
}
|
|
$summary[$key]['versata'] += $importo;
|
|
$totVersata += $importo;
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
}
|
|
|
|
$rowsOut = collect($summary)
|
|
->map(function (array $row) {
|
|
$fatta = round((float) ($row['fatta'] ?? 0), 2);
|
|
$versata = round((float) ($row['versata'] ?? 0), 2);
|
|
$row['fatta'] = $fatta;
|
|
$row['versata'] = $versata;
|
|
$row['delta'] = round($fatta - $versata, 2);
|
|
return $row;
|
|
})
|
|
->sortBy(['mese', 'tributo'])
|
|
->values()
|
|
->all();
|
|
|
|
return [
|
|
'rows' => $rowsOut,
|
|
'totale_fatta' => round($totFatta, 2),
|
|
'totale_versata' => round($totVersata, 2),
|
|
'totale_delta' => round($totFatta - $totVersata, 2),
|
|
];
|
|
}
|
|
|
|
public function getFattureRicevuteConsolidateProperty()
|
|
{
|
|
if (! Schema::hasTable('contabilita_fatture_fornitori')) {
|
|
return null;
|
|
}
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId(Auth::user());
|
|
if (! $activeStabileId) {
|
|
return null;
|
|
}
|
|
|
|
$query = DB::table('contabilita_fatture_fornitori as ff')
|
|
->leftJoin('fornitori as f', 'f.id', '=', 'ff.fornitore_id')
|
|
->leftJoin('fatture_elettroniche as fe', 'fe.id', '=', 'ff.fattura_elettronica_id')
|
|
->where('ff.stabile_id', (int) $activeStabileId);
|
|
|
|
if ($this->filterAnno) {
|
|
$query->whereYear('ff.data_documento', (int) $this->filterAnno);
|
|
}
|
|
|
|
if (! empty($this->filterCodForn)) {
|
|
$needle = trim((string) $this->filterCodForn);
|
|
$query->where(function ($q) use ($needle): void {
|
|
$q->where('f.old_id', $needle)
|
|
->orWhere('f.old_id', ltrim($needle, '0'));
|
|
});
|
|
}
|
|
|
|
if (! empty($this->search)) {
|
|
$needle = '%' . trim((string) $this->search) . '%';
|
|
$query->where(function ($q) use ($needle): void {
|
|
$q->orWhere('ff.numero_documento', 'like', $needle)
|
|
->orWhere('ff.descrizione', 'like', $needle)
|
|
->orWhere('f.ragione_sociale', 'like', $needle)
|
|
->orWhere('fe.sdi_file', 'like', $needle);
|
|
});
|
|
}
|
|
|
|
return $query
|
|
->select([
|
|
'ff.id',
|
|
'ff.data_documento',
|
|
'ff.numero_documento',
|
|
'ff.totale',
|
|
'ff.netto_da_pagare',
|
|
'ff.stato',
|
|
'ff.data_pagamento',
|
|
'ff.fattura_elettronica_id',
|
|
'ff.ritenuta_importo',
|
|
'ff.data_versamento_ra',
|
|
'f.old_id as cod_fornitore_legacy',
|
|
'f.ragione_sociale as fornitore_nome',
|
|
'fe.sdi_file as fe_sdi_file',
|
|
])
|
|
->orderByDesc('ff.data_documento')
|
|
->orderByDesc('ff.id')
|
|
->paginate($this->perPage, ['*'], 'fattureRicevutePage');
|
|
}
|
|
|
|
public function getFattureLegacyProperty()
|
|
{
|
|
$sourceTable = null;
|
|
if (Schema::hasTable('stg_fatture_gescon')) {
|
|
$sourceTable = 'stg_fatture_gescon';
|
|
} elseif (Schema::connection('gescon_import')->hasTable('fatture')) {
|
|
$sourceTable = 'fatture';
|
|
}
|
|
|
|
if (! $sourceTable) {
|
|
return null;
|
|
}
|
|
|
|
if ($sourceTable === 'stg_fatture_gescon') {
|
|
$query = DB::table('stg_fatture_gescon as f');
|
|
} else {
|
|
$query = DB::connection('gescon_import')->table('fatture as f');
|
|
}
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId(Auth::user());
|
|
$activeTenantId = null;
|
|
|
|
$legacyStabileIdCandidates = [];
|
|
$legacyCodiceCandidates = [];
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if ($legacyCode) {
|
|
if (Schema::hasTable('stabili')) {
|
|
$stabileRow = DB::table('stabili')
|
|
->where('codice_stabile', $legacyCode)
|
|
->first(['id', 'old_id', 'cod_stabile', 'codice_stabile', 'amministratore_id']);
|
|
|
|
if ($stabileRow && ! empty($stabileRow->amministratore_id)) {
|
|
$activeTenantId = (string) $stabileRow->amministratore_id;
|
|
}
|
|
|
|
foreach ([(string) ($stabileRow->old_id ?? ''), (string) ($stabileRow->cod_stabile ?? ''), (string) ($stabileRow->codice_stabile ?? $legacyCode)] as $value) {
|
|
$trim = trim($value);
|
|
if ($trim === '') {
|
|
continue;
|
|
}
|
|
|
|
$legacyCodiceCandidates[] = $trim;
|
|
$noZero = ltrim($trim, '0');
|
|
if ($noZero !== '') {
|
|
$legacyCodiceCandidates[] = $noZero;
|
|
}
|
|
|
|
if (is_numeric($trim)) {
|
|
$legacyStabileIdCandidates[] = (int) $trim;
|
|
$normalized = ltrim($trim, '0');
|
|
if ($normalized !== '' && is_numeric($normalized)) {
|
|
$legacyStabileIdCandidates[] = (int) $normalized;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$legacyCodiceCandidates[] = $legacyCode;
|
|
$noZero = ltrim($legacyCode, '0');
|
|
if ($noZero !== '') {
|
|
$legacyCodiceCandidates[] = $noZero;
|
|
}
|
|
if (is_numeric($legacyCode)) {
|
|
$legacyStabileIdCandidates[] = (int) $legacyCode;
|
|
}
|
|
}
|
|
|
|
$legacyStabileIdCandidates = array_values(array_unique(array_filter($legacyStabileIdCandidates, fn($v) => is_int($v) && $v > 0)));
|
|
$legacyCodiceCandidates = array_values(array_unique(array_filter($legacyCodiceCandidates, fn($v) => is_string($v) && $v !== '')));
|
|
|
|
if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'legacy_id_stabile') && ! empty($legacyStabileIdCandidates)) {
|
|
$query->whereIn('f.legacy_id_stabile', $legacyStabileIdCandidates);
|
|
} elseif ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'cod_stabile') && ! empty($legacyCodiceCandidates)) {
|
|
$query->whereIn('f.cod_stabile', $legacyCodiceCandidates);
|
|
}
|
|
}
|
|
|
|
if ($sourceTable === 'stg_fatture_gescon' && $activeTenantId !== null && Schema::hasColumn('stg_fatture_gescon', 'tenant_id')) {
|
|
$query->where('f.tenant_id', $activeTenantId);
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$year = (int) $this->filterAnno;
|
|
if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'data_fattura')) {
|
|
$query->whereYear('f.data_fattura', $year);
|
|
} elseif ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'data_fattura')) {
|
|
$query->whereYear('f.data_fattura', $year);
|
|
}
|
|
}
|
|
|
|
if (! empty($this->filterCodForn)) {
|
|
if ($sourceTable === 'stg_fatture_gescon' && Schema::hasColumn('stg_fatture_gescon', 'cod_fornitore')) {
|
|
$query->where('f.cod_fornitore', trim((string) $this->filterCodForn));
|
|
}
|
|
if ($sourceTable === 'fatture' && Schema::connection('gescon_import')->hasColumn('fatture', 'cod_fornitore')) {
|
|
$query->where('f.cod_fornitore', trim((string) $this->filterCodForn));
|
|
}
|
|
}
|
|
|
|
if (! empty($this->search)) {
|
|
$needle = '%' . trim((string) $this->search) . '%';
|
|
$query->where(function ($q) use ($needle, $sourceTable): void {
|
|
$fields = ['numero_fattura', 'descrizione', 'descrizione_sintetica', 'descrizione_corpo', 'note'];
|
|
if ($sourceTable === 'stg_fatture_gescon') {
|
|
$fields[] = 'cod_fornitore';
|
|
} else {
|
|
$fields[] = 'cod_fornitore';
|
|
}
|
|
|
|
foreach ($fields as $field) {
|
|
if ($sourceTable === 'stg_fatture_gescon' && ! Schema::hasColumn('stg_fatture_gescon', $field)) {
|
|
continue;
|
|
}
|
|
if ($sourceTable === 'fatture' && ! Schema::connection('gescon_import')->hasColumn('fatture', $field)) {
|
|
continue;
|
|
}
|
|
$q->orWhere('f.' . $field, 'like', $needle);
|
|
}
|
|
});
|
|
}
|
|
|
|
if ($sourceTable === 'stg_fatture_gescon') {
|
|
$select = [
|
|
'f.id as row_id',
|
|
'f.legacy_id_fatture',
|
|
'f.cod_fornitore',
|
|
'f.numero_fattura',
|
|
'f.data_fattura',
|
|
'f.data_pagamento',
|
|
'f.descrizione_sintetica',
|
|
'f.descrizione_corpo',
|
|
'f.note',
|
|
'f.imponibile',
|
|
'f.importo_iva',
|
|
'f.totale_fattura',
|
|
'f.importo_netto',
|
|
'f.importo_rda',
|
|
'f.riferimento',
|
|
'f.reg_gestione',
|
|
'f.reg_nstra',
|
|
];
|
|
} else {
|
|
$select = [
|
|
'f.id as row_id',
|
|
'f.id as legacy_id_fatture',
|
|
'f.cod_fornitore',
|
|
'f.numero_fattura',
|
|
'f.data_fattura',
|
|
'f.data_pagamento',
|
|
'f.descrizione',
|
|
DB::raw('NULL as descrizione_corpo'),
|
|
'f.note',
|
|
DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as imponibile'),
|
|
'f.iva as importo_iva',
|
|
DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as totale_fattura'),
|
|
DB::raw('COALESCE(f.importo_totale, f.importo_euro, 0) as importo_netto'),
|
|
'f.ritenuta as importo_rda',
|
|
DB::raw('NULL as riferimento'),
|
|
DB::raw('NULL as reg_gestione'),
|
|
DB::raw('NULL as reg_nstra'),
|
|
];
|
|
}
|
|
|
|
$paginator = $query
|
|
->select($select)
|
|
->orderByDesc('f.data_fattura')
|
|
->orderByDesc('f.id')
|
|
->paginate($this->perPage);
|
|
|
|
$codFornitori = $paginator->getCollection()
|
|
->pluck('cod_fornitore')
|
|
->filter(fn($v) => $v !== null && trim((string) $v) !== '')
|
|
->map(fn($v) => trim((string) $v))
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$fornitoriMap = [];
|
|
$fornitoriIdMap = [];
|
|
$fornitoriTributoMap = [];
|
|
|
|
if (! empty($codFornitori)) {
|
|
if (Schema::hasTable('stg_fornitori_gescon')) {
|
|
$fornRows = DB::table('stg_fornitori_gescon')
|
|
->whereIn('cod_forn', $codFornitori)
|
|
->get(['cod_forn', 'denominazione', 'cognome', 'nome', 'raw']);
|
|
} else {
|
|
$fornRows = DB::connection('gescon_import')
|
|
->table('mdb_fornitori')
|
|
->whereIn('cod_forn', $codFornitori)
|
|
->get(['cod_forn', 'denominazione', 'cognome', 'nome']);
|
|
}
|
|
|
|
foreach ($fornRows as $row) {
|
|
$code = trim((string) ($row->cod_forn ?? ''));
|
|
if ($code === '') {
|
|
continue;
|
|
}
|
|
$label = trim((string) ($row->denominazione ?? ''));
|
|
if ($label === '') {
|
|
$label = trim((string) (($row->nome ?? '') . ' ' . ($row->cognome ?? '')));
|
|
}
|
|
$fornitoriMap[$code] = $label !== '' ? $label : null;
|
|
|
|
$rawTributo = null;
|
|
if (isset($row->raw)) {
|
|
$raw = json_decode((string) $row->raw, true);
|
|
if (is_array($raw)) {
|
|
$rawTributo = strtoupper(trim((string) ($raw['trib_1019_1020'] ?? '')));
|
|
}
|
|
}
|
|
|
|
if ($rawTributo !== '') {
|
|
$fornitoriTributoMap[$code] = $rawTributo;
|
|
$codeNoZero = ltrim($code, '0');
|
|
if ($codeNoZero !== '') {
|
|
$fornitoriTributoMap[$codeNoZero] = $rawTributo;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Schema::hasTable('fornitori')) {
|
|
$localRows = DB::table('fornitori')
|
|
->whereIn('old_id', $codFornitori)
|
|
->get(['id', 'old_id']);
|
|
foreach ($localRows as $local) {
|
|
$fornitoriIdMap[(string) $local->old_id] = (int) $local->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
$fattureLocali = [];
|
|
$fattureFeByDoc = [];
|
|
$fattureFeByFornitoreDoc = [];
|
|
if (! empty($fornitoriIdMap) && Schema::hasTable('contabilita_fatture_fornitori')) {
|
|
$fattureRows = DB::table('contabilita_fatture_fornitori')
|
|
->when($activeStabileId, fn($q) => $q->where('stabile_id', (int) $activeStabileId))
|
|
->whereIn('fornitore_id', array_values($fornitoriIdMap))
|
|
->get(['id', 'fornitore_id', 'fattura_elettronica_id', 'numero_documento', 'data_documento', 'totale', 'stato', 'data_versamento_ra', 'ritenuta_importo', 'ritenuta_codice_tributo']);
|
|
|
|
foreach ($fattureRows as $fattura) {
|
|
$numero = $this->normalizeInvoiceNumber($fattura->numero_documento ?? null);
|
|
$data = ! empty($fattura->data_documento)
|
|
? (string) \Carbon\Carbon::parse($fattura->data_documento)->format('Y-m-d')
|
|
: null;
|
|
if (! $numero || ! $data) {
|
|
continue;
|
|
}
|
|
$key = (int) $fattura->fornitore_id . '|' . $numero . '|' . $data;
|
|
$fattureLocali[$key][] = $fattura;
|
|
|
|
$globalKey = $numero . '|' . $data;
|
|
$fattureFeByDoc[$globalKey][] = $fattura;
|
|
$fattureFeByFornitoreDoc[$key][] = $fattura;
|
|
}
|
|
}
|
|
|
|
$feGlobalByDoc = [];
|
|
$feByFornitoreDoc = [];
|
|
if ($activeStabileId && Schema::hasTable('fatture_elettroniche')) {
|
|
$feRows = DB::table('fatture_elettroniche')
|
|
->where('stabile_id', (int) $activeStabileId)
|
|
->get(['id', 'fornitore_id', 'numero_fattura', 'data_fattura']);
|
|
|
|
foreach ($feRows as $fe) {
|
|
$numero = $this->normalizeInvoiceNumber($fe->numero_fattura ?? null);
|
|
$data = ! empty($fe->data_fattura)
|
|
? (string) \Carbon\Carbon::parse($fe->data_fattura)->format('Y-m-d')
|
|
: null;
|
|
if (! $numero || ! $data) {
|
|
continue;
|
|
}
|
|
|
|
$globalKey = $numero . '|' . $data;
|
|
$feGlobalByDoc[$globalKey][] = $fe;
|
|
|
|
$fornitoreId = is_numeric($fe->fornitore_id ?? null) ? (int) $fe->fornitore_id : 0;
|
|
if ($fornitoreId > 0) {
|
|
$key = $fornitoreId . '|' . $numero . '|' . $data;
|
|
$feByFornitoreDoc[$key][] = $fe;
|
|
}
|
|
}
|
|
}
|
|
|
|
$legacyRitenutaById = [];
|
|
if ($sourceTable === 'stg_fatture_gescon' && Schema::connection('gescon_import')->hasTable('fatture')) {
|
|
$legacyIds = $paginator->getCollection()
|
|
->pluck('legacy_id_fatture')
|
|
->filter(fn($v) => is_numeric($v))
|
|
->map(fn($v) => (int) $v)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
if (! empty($legacyIds)) {
|
|
$legacyRitenutaById = DB::connection('gescon_import')
|
|
->table('fatture')
|
|
->whereIn('id', $legacyIds)
|
|
->get(['id', 'ritenuta'])
|
|
->mapWithKeys(fn($r) => [(int) $r->id => $this->normalizeMoney($r->ritenuta ?? 0)])
|
|
->all();
|
|
}
|
|
}
|
|
|
|
$rdaCodeByInvoice = [];
|
|
$rdaRowsByCode = [];
|
|
if (Schema::connection('gescon_import')->hasTable('operazioni')) {
|
|
$opQuery = DB::connection('gescon_import')->table('operazioni');
|
|
|
|
if (! empty($legacyCodiceCandidates) && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) {
|
|
$opQuery->whereIn('cod_stabile', $legacyCodiceCandidates);
|
|
}
|
|
|
|
if ($this->filterAnno && Schema::connection('gescon_import')->hasColumn('operazioni', 'dt_spe')) {
|
|
$opQuery->whereYear('dt_spe', (int) $this->filterAnno);
|
|
}
|
|
|
|
$opSelect = ['benef'];
|
|
foreach (['rif_rda', 'dt_spe', 'cod_for', 'num_fat', 'netto_vers_rda', 'importo_euro_770', 'importo_euro', 'importo'] as $column) {
|
|
if (Schema::connection('gescon_import')->hasColumn('operazioni', $column)) {
|
|
$opSelect[] = $column;
|
|
}
|
|
}
|
|
|
|
$opRows = $opQuery->get(array_values(array_unique($opSelect)));
|
|
|
|
foreach ($opRows as $opRow) {
|
|
$benef = trim((string) ($opRow->benef ?? ''));
|
|
$benefUpper = strtoupper($benef);
|
|
$isRdaLine = $benefUpper !== '' && str_starts_with($benefUpper, 'RDA:');
|
|
|
|
$rdaCode = trim((string) ($opRow->rif_rda ?? ''));
|
|
if ($rdaCode === '' && $isRdaLine) {
|
|
$rdaCode = trim(substr($benef, 4));
|
|
}
|
|
|
|
if ($rdaCode === '') {
|
|
continue;
|
|
}
|
|
|
|
if ($isRdaLine) {
|
|
$rdaAmount = abs($this->normalizeMoney(
|
|
$opRow->netto_vers_rda ?? $opRow->importo_euro_770 ?? $opRow->importo_euro ?? $opRow->importo ?? 0
|
|
));
|
|
|
|
$existingDate = $rdaRowsByCode[$rdaCode]['data'] ?? null;
|
|
$currentDate = ! empty($opRow->dt_spe) ? (string)
|
|
\Carbon\Carbon::parse($opRow->dt_spe)->format('Y-m-d') : null;
|
|
|
|
if (! isset($rdaRowsByCode[$rdaCode])) {
|
|
$rdaRowsByCode[$rdaCode] = [
|
|
'importo' => $rdaAmount,
|
|
'data' => $currentDate,
|
|
];
|
|
} elseif ($currentDate && ($existingDate === null || $currentDate >= (string) $existingDate)) {
|
|
$rdaRowsByCode[$rdaCode]['data'] = $currentDate;
|
|
if ($rdaAmount > 0) {
|
|
$rdaRowsByCode[$rdaCode]['importo'] = $rdaAmount;
|
|
}
|
|
} elseif (($rdaRowsByCode[$rdaCode]['importo'] ?? 0) <= 0 && $rdaAmount > 0) {
|
|
$rdaRowsByCode[$rdaCode]['importo'] = $rdaAmount;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$numeroNorm = $this->normalizeInvoiceNumber($opRow->num_fat ?? null);
|
|
if ($numeroNorm === null) {
|
|
continue;
|
|
}
|
|
|
|
$codForRaw = trim((string) ($opRow->cod_for ?? ''));
|
|
if ($codForRaw === '') {
|
|
continue;
|
|
}
|
|
|
|
$fornCandidates = array_values(array_unique(array_filter([
|
|
$codForRaw,
|
|
ltrim($codForRaw, '0'),
|
|
], fn($v) => is_string($v) && $v !== '')));
|
|
|
|
foreach ($fornCandidates as $fornCode) {
|
|
$rdaCodeByInvoice[$fornCode . '|' . $numeroNorm] = $rdaCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
$paginator->setCollection(
|
|
$paginator->getCollection()->map(function ($row) use ($fornitoriMap, $fornitoriIdMap, $fornitoriTributoMap, $fattureLocali, $fattureFeByDoc, $fattureFeByFornitoreDoc, $legacyRitenutaById, $rdaCodeByInvoice, $rdaRowsByCode, $feGlobalByDoc, $feByFornitoreDoc) {
|
|
$code = trim((string) ($row->cod_fornitore ?? ''));
|
|
$row->fornitore_nome = $fornitoriMap[$code] ?? null;
|
|
$row->fornitore_id = $fornitoriIdMap[$code] ?? null;
|
|
|
|
$tributoFromFornitore = null;
|
|
$codeNoZero = ltrim($code, '0');
|
|
if ($code !== '' && ! empty($fornitoriTributoMap[$code])) {
|
|
$tributoFromFornitore = $fornitoriTributoMap[$code];
|
|
} elseif ($codeNoZero !== '' && ! empty($fornitoriTributoMap[$codeNoZero])) {
|
|
$tributoFromFornitore = $fornitoriTributoMap[$codeNoZero];
|
|
}
|
|
|
|
$descrizione = trim((string) ($row->descrizione_sintetica ?? ''));
|
|
if ($descrizione === '') {
|
|
$descrizione = trim((string) ($row->descrizione ?? ''));
|
|
}
|
|
if ($descrizione === '') {
|
|
$descrizione = trim((string) ($row->descrizione_corpo ?? ''));
|
|
}
|
|
$row->descrizione_view = $descrizione !== '' ? $descrizione : '—';
|
|
|
|
$row->totale_view = $this->normalizeMoney($row->totale_fattura ?? $row->importo_netto ?? $row->imponibile ?? 0);
|
|
$row->ra_fatta_importo = abs($this->normalizeMoney($row->importo_rda ?? 0));
|
|
$row->ra_versata_data = null;
|
|
$row->ra_tributo = null;
|
|
$row->ra_riferimento = null;
|
|
|
|
$legacyId = is_numeric($row->legacy_id_fatture ?? null) ? (int) $row->legacy_id_fatture : null;
|
|
if ($row->ra_fatta_importo <= 0 && $legacyId && isset($legacyRitenutaById[$legacyId])) {
|
|
$row->ra_fatta_importo = abs($this->normalizeMoney($legacyRitenutaById[$legacyId]));
|
|
}
|
|
|
|
$row->local_match_id = null;
|
|
$row->local_match_url = null;
|
|
$row->local_match_stato = null;
|
|
$row->local_match_fe_id = null;
|
|
$row->local_match_fe_url = null;
|
|
|
|
$numero = $this->normalizeInvoiceNumber($row->numero_fattura ?? null);
|
|
$data = ! empty($row->data_fattura)
|
|
? (string) \Carbon\Carbon::parse($row->data_fattura)->format('Y-m-d')
|
|
: null;
|
|
|
|
if ($numero !== null) {
|
|
$fornCandidates = array_values(array_unique(array_filter([
|
|
$code,
|
|
ltrim($code, '0'),
|
|
], fn($v) => is_string($v) && $v !== '')));
|
|
|
|
foreach ($fornCandidates as $fornCode) {
|
|
$key = $fornCode . '|' . $numero;
|
|
if (isset($rdaCodeByInvoice[$key])) {
|
|
$row->ra_riferimento = $rdaCodeByInvoice[$key];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($row->ra_riferimento) && ! empty($row->riferimento)) {
|
|
$row->ra_riferimento = trim((string) $row->riferimento);
|
|
}
|
|
|
|
if (! empty($row->ra_riferimento) && isset($rdaRowsByCode[$row->ra_riferimento])) {
|
|
$rdaData = $rdaRowsByCode[$row->ra_riferimento];
|
|
if ($row->ra_fatta_importo <= 0) {
|
|
$row->ra_fatta_importo = abs($this->normalizeMoney($rdaData['importo'] ?? 0));
|
|
}
|
|
if (empty($row->ra_versata_data) && ! empty($rdaData['data'])) {
|
|
$row->ra_versata_data = $rdaData['data'];
|
|
}
|
|
}
|
|
|
|
if ($row->fornitore_id && $numero && $data) {
|
|
$key = (int) $row->fornitore_id . '|' . $numero . '|' . $data;
|
|
$match = $fattureLocali[$key][0] ?? null;
|
|
if ($match) {
|
|
$row->local_match_id = (int) ($match->id ?? 0);
|
|
$row->local_match_stato = $match->stato ?? null;
|
|
$row->ra_versata_data = $match->data_versamento_ra ?? null;
|
|
$row->ra_tributo = $match->ritenuta_codice_tributo ?? null;
|
|
if ($row->ra_fatta_importo <= 0) {
|
|
$row->ra_fatta_importo = abs($this->normalizeMoney($match->ritenuta_importo ?? 0));
|
|
}
|
|
$row->local_match_url = \App\Filament\Pages\Contabilita\FatturaFornitoreScheda::getUrl([
|
|
'record' => (int) $match->id,
|
|
], panel: 'admin-filament');
|
|
|
|
$feId = is_numeric($match->fattura_elettronica_id ?? null) ? (int) $match->fattura_elettronica_id : 0;
|
|
if ($feId > 0) {
|
|
$row->local_match_fe_id = $feId;
|
|
$row->local_match_fe_url = \App\Filament\Pages\Contabilita\FatturaElettronicaScheda::getUrl([
|
|
'record' => $feId,
|
|
], panel: 'admin-filament');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: FE presente ma fattura contabile non ancora agganciata.
|
|
if (empty($row->local_match_url) && $numero && $data) {
|
|
$feMatch = null;
|
|
|
|
if ($row->fornitore_id) {
|
|
$strictKey = (int) $row->fornitore_id . '|' . $numero . '|' . $data;
|
|
$strict = $feByFornitoreDoc[$strictKey] ?? [];
|
|
if (count($strict) === 1) {
|
|
$feMatch = $strict[0];
|
|
}
|
|
}
|
|
|
|
if (! $feMatch) {
|
|
$globalKey = $numero . '|' . $data;
|
|
$global = $feGlobalByDoc[$globalKey] ?? [];
|
|
if (count($global) === 1) {
|
|
$feMatch = $global[0];
|
|
}
|
|
}
|
|
|
|
if ($feMatch && is_numeric($feMatch->id ?? null)) {
|
|
$row->local_match_fe_id = (int) $feMatch->id;
|
|
$row->local_match_fe_url = \App\Filament\Pages\Contabilita\FatturaElettronicaScheda::getUrl([
|
|
'record' => (int) $feMatch->id,
|
|
], panel: 'admin-filament');
|
|
}
|
|
}
|
|
|
|
if (empty($row->ra_tributo) && ! empty($tributoFromFornitore)) {
|
|
$row->ra_tributo = $tributoFromFornitore;
|
|
}
|
|
|
|
if ($row->ra_fatta_importo > 0 && empty($row->ra_tributo)) {
|
|
$row->ra_tributo = '1040';
|
|
}
|
|
|
|
return $row;
|
|
})
|
|
);
|
|
|
|
return $paginator;
|
|
}
|
|
|
|
private function resolveTributoByFornitoreIdMap(array $fornitoreIds): array
|
|
{
|
|
if (empty($fornitoreIds) || ! Schema::hasTable('fornitori') || ! Schema::hasTable('stg_fornitori_gescon')) {
|
|
return [];
|
|
}
|
|
|
|
$fornitoriRows = DB::table('fornitori')
|
|
->whereIn('id', $fornitoreIds)
|
|
->get(['id', 'old_id']);
|
|
|
|
$oldIds = $fornitoriRows
|
|
->pluck('old_id')
|
|
->filter(fn($v) => $v !== null && trim((string) $v) !== '')
|
|
->map(fn($v) => trim((string) $v))
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
if (empty($oldIds)) {
|
|
return [];
|
|
}
|
|
|
|
$stgRows = DB::table('stg_fornitori_gescon')
|
|
->whereIn('cod_forn', $oldIds)
|
|
->get(['cod_forn', 'raw']);
|
|
|
|
$tributoByCodForn = [];
|
|
foreach ($stgRows as $stg) {
|
|
$cod = trim((string) ($stg->cod_forn ?? ''));
|
|
if ($cod === '') {
|
|
continue;
|
|
}
|
|
|
|
$raw = json_decode((string) ($stg->raw ?? ''), true);
|
|
if (! is_array($raw)) {
|
|
continue;
|
|
}
|
|
|
|
$tributo = strtoupper(trim((string) ($raw['trib_1019_1020'] ?? '')));
|
|
if ($tributo !== '') {
|
|
$tributoByCodForn[$cod] = $tributo;
|
|
$codNoZero = ltrim($cod, '0');
|
|
if ($codNoZero !== '') {
|
|
$tributoByCodForn[$codNoZero] = $tributo;
|
|
}
|
|
}
|
|
}
|
|
|
|
$out = [];
|
|
foreach ($fornitoriRows as $fornitore) {
|
|
$oldId = trim((string) ($fornitore->old_id ?? ''));
|
|
if ($oldId === '') {
|
|
continue;
|
|
}
|
|
|
|
$oldIdNoZero = ltrim($oldId, '0');
|
|
$tributo = $tributoByCodForn[$oldId] ?? ($oldIdNoZero !== '' ? ($tributoByCodForn[$oldIdNoZero] ?? null) : null);
|
|
if ($tributo) {
|
|
$out[(int) $fornitore->id] = $tributo;
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public function importaFattureLegacy(): void
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user) {
|
|
abort(403);
|
|
}
|
|
|
|
if (! Schema::hasTable('contabilita_fatture_fornitori')) {
|
|
Notification::make()
|
|
->title('Tabella fatture fornitori non disponibile')
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$activeStabileId = StabileContext::resolveActiveStabileId($user);
|
|
if (! $activeStabileId) {
|
|
Notification::make()
|
|
->title('Seleziona uno stabile attivo')
|
|
->warning()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$tenantId = null;
|
|
if (Schema::hasTable('stabili')) {
|
|
$tenantId = DB::table('stabili')
|
|
->where('id', (int) $activeStabileId)
|
|
->value('amministratore_id');
|
|
}
|
|
|
|
if (! $tenantId && isset($user->amministratore_id) && is_numeric($user->amministratore_id)) {
|
|
$tenantId = (int) $user->amministratore_id;
|
|
}
|
|
|
|
if (! $tenantId) {
|
|
Notification::make()
|
|
->title('Amministratore non risolto')
|
|
->body('Impossibile risolvere il tenant per l\'import fatture.')
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$params = [
|
|
'tenant' => (string) $tenantId,
|
|
'--stabile-id' => (string) $activeStabileId,
|
|
];
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if (! empty($legacyCode)) {
|
|
$params['--stabile-code'] = $legacyCode;
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$params['--anno'] = (string) $this->filterAnno;
|
|
}
|
|
|
|
try {
|
|
$exit = Artisan::call('gescon:sync-fatture-fornitori', $params);
|
|
$output = trim((string) Artisan::output());
|
|
} catch (\Throwable $e) {
|
|
Notification::make()
|
|
->title('Import fatture fallito')
|
|
->body($e->getMessage())
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
if ($exit === 0 && str_contains($output, 'Nessuna fattura da sincronizzare.')) {
|
|
$mdbPath = $this->resolveLegacyGeneraleMdbPath((int) $activeStabileId);
|
|
if (! empty($mdbPath)) {
|
|
try {
|
|
$loadExit = Artisan::call('import:gescon-fatture-legacy', [
|
|
'tenant' => (string) $tenantId,
|
|
'--mdb' => $mdbPath,
|
|
]);
|
|
$loadOutput = trim((string) Artisan::output());
|
|
} catch (\Throwable $e) {
|
|
Notification::make()
|
|
->title('Import da generale_stabile.mdb fallito')
|
|
->body($e->getMessage())
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
if ($loadExit !== 0) {
|
|
$snippet = $loadOutput !== '' ? mb_substr($loadOutput, 0, 800) : 'Errore durante caricamento MDB.';
|
|
Notification::make()
|
|
->title('Import da generale_stabile.mdb: errore')
|
|
->body($snippet)
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$exit = Artisan::call('gescon:sync-fatture-fornitori', $params);
|
|
$output = trim((string) Artisan::output());
|
|
} catch (\Throwable $e) {
|
|
Notification::make()
|
|
->title('Sync post-import fallita')
|
|
->body($e->getMessage())
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($exit !== 0) {
|
|
$snippet = $output !== '' ? mb_substr($output, 0, 800) : 'Errore durante la sincronizzazione.';
|
|
Notification::make()
|
|
->title('Import fatture: errore')
|
|
->body($snippet)
|
|
->danger()
|
|
->send();
|
|
return;
|
|
}
|
|
|
|
$tail = $output !== '' ? trim((string) collect(preg_split('/\R/', $output) ?: [])->last()): null;
|
|
Notification::make()
|
|
->title('Import fatture completato')
|
|
->body($tail ?: 'Sincronizzazione eseguita correttamente.')
|
|
->success()
|
|
->send();
|
|
|
|
$this->resetPage();
|
|
}
|
|
|
|
private function resolveLegacyGeneraleMdbPath(int $stabileId): ?string
|
|
{
|
|
$base = (string) UserSetting::get('gescon.source_path', '/mnt/gescon-archives/gescon');
|
|
$base = rtrim($base, '/');
|
|
if ($base === '') {
|
|
$base = '/mnt/gescon-archives/gescon';
|
|
}
|
|
|
|
$stabile = Schema::hasTable('stabili')
|
|
? DB::table('stabili')->where('id', $stabileId)->first(['codice_stabile', 'cod_stabile', 'old_id'])
|
|
: null;
|
|
|
|
$codes = [];
|
|
foreach ([(string) ($stabile->codice_stabile ?? ''), (string) ($stabile->cod_stabile ?? ''), (string) ($stabile->old_id ?? '')] as $raw) {
|
|
$value = trim($raw);
|
|
if ($value === '') {
|
|
continue;
|
|
}
|
|
|
|
$codes[] = $value;
|
|
$codes[] = ltrim($value, '0');
|
|
|
|
if (is_numeric($value)) {
|
|
$num = (int) $value;
|
|
$codes[] = (string) $num;
|
|
$codes[] = str_pad((string) $num, 4, '0', STR_PAD_LEFT);
|
|
}
|
|
}
|
|
|
|
$codes = array_values(array_unique(array_filter($codes, fn($v) => is_string($v) && trim($v) !== '')));
|
|
foreach ($codes as $code) {
|
|
$path = $base . '/' . $code . '/generale_stabile.mdb';
|
|
if (is_file($path)) {
|
|
return $path;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function applyIncassiFilters($query): void
|
|
{
|
|
$codStabile = $this->resolveLegacyCodiceStabile();
|
|
if ($codStabile && Schema::connection('gescon_import')->hasColumn('incassi', 'cod_stabile')) {
|
|
$query->where('incassi.cod_stabile', $codStabile);
|
|
}
|
|
|
|
if ($this->onlyStraordinarieMode) {
|
|
if (Schema::connection('gescon_import')->hasColumn('incassi', 'o_r_s')) {
|
|
$query->where('incassi.o_r_s', 'S');
|
|
}
|
|
if ($this->selectedStraordinaria !== null && Schema::connection('gescon_import')->hasColumn('incassi', 'n_stra')) {
|
|
$query->where('incassi.n_stra', $this->selectedStraordinaria);
|
|
}
|
|
} else {
|
|
if (Schema::connection('gescon_import')->hasColumn('incassi', 'o_r_s')) {
|
|
$query->where(function ($qq): void {
|
|
$qq->where('incassi.o_r_s', 'O')
|
|
->orWhereNull('incassi.o_r_s')
|
|
->orWhere('incassi.o_r_s', '');
|
|
});
|
|
}
|
|
}
|
|
|
|
$anno = $this->legacyGestioneAnno;
|
|
if ($anno) {
|
|
$annoCol = Schema::connection('gescon_import')->hasColumn('incassi', 'anno')
|
|
? 'anno'
|
|
: (Schema::connection('gescon_import')->hasColumn('incassi', 'anno_rif') ? 'anno_rif' : null);
|
|
$annoShort = (int) substr((string) $anno, -2);
|
|
|
|
$query->where(function ($qq) use ($annoCol, $anno, $annoShort): void {
|
|
if ($annoCol) {
|
|
$qq->where($annoCol, (string) $anno)
|
|
->orWhere($annoCol, (string) $annoShort)
|
|
->orWhere($annoCol, str_pad((string) $annoShort, 2, '0', STR_PAD_LEFT));
|
|
}
|
|
if (Schema::connection('gescon_import')->hasColumn('incassi', 'dt_empag')) {
|
|
$qq->orWhereYear('dt_empag', (int) $anno);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (! empty($this->search)) {
|
|
$needle = trim((string) $this->search);
|
|
$query->where(function ($qq) use ($needle): void {
|
|
foreach (['cod_cond', 'descrizione', 'cod_cassa', 'n_ricevuta', 'n_riferimento', 'riferimento_pagamento'] as $col) {
|
|
if (Schema::connection('gescon_import')->hasColumn('incassi', $col)) {
|
|
$qq->orWhere($col, 'like', '%' . $needle . '%');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private function resolveIncassoImporto(object $row): float
|
|
{
|
|
$importoEuro = $this->normalizeMoney($row->importo_euro ?? null);
|
|
if (abs($importoEuro) > 0.00001) {
|
|
return round($importoEuro, 2);
|
|
}
|
|
|
|
$importo = $this->normalizeMoney($row->importo ?? null);
|
|
if (abs($importo) > 0.00001) {
|
|
return round($importo, 2);
|
|
}
|
|
|
|
$pagato = $this->normalizeMoney($row->importo_pagato_euro ?? null);
|
|
$abs = abs($pagato);
|
|
if ($abs >= 10000 && fmod($abs, 1.0) === 0.0) {
|
|
return round($pagato / 10000, 2);
|
|
}
|
|
if ($abs >= 1000 && fmod($abs, 1.0) === 0.0 && ((int) $abs % 100) === 0) {
|
|
return round($pagato / 100, 2);
|
|
}
|
|
|
|
return round($pagato, 2);
|
|
}
|
|
|
|
private function relationScoreForIncasso(string $tipoRelazione, string $ruolo): int
|
|
{
|
|
$tipo = strtolower(trim($tipoRelazione));
|
|
|
|
if ($ruolo === 'I') {
|
|
return match ($tipo) {
|
|
'inquilino', 'conduttore', 'affittuario', 'locatario' => 100,
|
|
default => 10,
|
|
};
|
|
}
|
|
|
|
return match ($tipo) {
|
|
'proprietario' => 100,
|
|
'comproprietario' => 90,
|
|
'usufruttuario' => 80,
|
|
'delegato_assemblea' => 70,
|
|
'referente' => 60,
|
|
default => 10,
|
|
};
|
|
}
|
|
|
|
private function findRateMatchForIncasso(object $incasso, array $rateRows): ?array
|
|
{
|
|
$refs = [];
|
|
foreach (['n_rata_riferimento', 'riferimento_pagamento', 'n_riferimento', 'n_ricevuta'] as $field) {
|
|
$v = $incasso->{$field} ?? null;
|
|
if (is_numeric($v)) {
|
|
$refs[] = (int) $v;
|
|
} elseif (is_string($v)) {
|
|
$trim = trim($v);
|
|
if (ctype_digit($trim)) {
|
|
$refs[] = (int) $trim;
|
|
}
|
|
}
|
|
}
|
|
$refs = array_values(array_unique($refs));
|
|
|
|
foreach ($rateRows as $rate) {
|
|
$nEmissione = is_numeric($rate->n_emissione ?? null) ? (int) $rate->n_emissione : null;
|
|
if ($nEmissione !== null && in_array($nEmissione, $refs, true)) {
|
|
return [
|
|
'n_emissione' => $nEmissione,
|
|
'importo' => (float) ($rate->importo_euro ?? $rate->importo ?? 0),
|
|
'data_emissione' => $rate->data_emissione ?? null,
|
|
'causale' => $rate->causale ?? null,
|
|
'tipo_rata' => $rate->tipo_rata ?? null,
|
|
'pagata' => (bool) ($rate->pagata ?? false),
|
|
];
|
|
}
|
|
}
|
|
|
|
$incassoImporto = abs($this->resolveIncassoImporto($incasso));
|
|
if ($incassoImporto <= 0.0) {
|
|
return null;
|
|
}
|
|
|
|
$best = null;
|
|
foreach ($rateRows as $rate) {
|
|
$rateImporto = abs($this->normalizeMoney($rate->importo_euro ?? $rate->importo ?? 0));
|
|
if (abs($incassoImporto - $rateImporto) > 0.01) {
|
|
continue;
|
|
}
|
|
|
|
$score = 10;
|
|
$incassoDate = ! empty($incasso->data_visuale) ? (string) $incasso->data_visuale : null;
|
|
$rateDate = ! empty($rate->data_scadenza) ? (string) $rate->data_scadenza : (! empty($rate->data_emissione) ? (string) $rate->data_emissione : null);
|
|
if ($incassoDate && $rateDate) {
|
|
try {
|
|
$d1 = \Carbon\Carbon::parse($incassoDate);
|
|
$d2 = \Carbon\Carbon::parse($rateDate);
|
|
$days = abs($d1->diffInDays($d2));
|
|
$score += max(0, 30 - min($days, 30));
|
|
} catch (\Throwable) {
|
|
}
|
|
}
|
|
|
|
if (! $best || $score > ($best['score'] ?? -1)) {
|
|
$best = [
|
|
'score' => $score,
|
|
'n_emissione' => is_numeric($rate->n_emissione ?? null) ? (int) $rate->n_emissione : null,
|
|
'importo' => (float) ($rate->importo_euro ?? $rate->importo ?? 0),
|
|
'data_emissione' => $rate->data_emissione ?? null,
|
|
'causale' => $rate->causale ?? null,
|
|
'tipo_rata' => $rate->tipo_rata ?? null,
|
|
'pagata' => (bool) ($rate->pagata ?? false),
|
|
];
|
|
}
|
|
}
|
|
|
|
if (! $best) {
|
|
return null;
|
|
}
|
|
|
|
unset($best['score']);
|
|
return $best;
|
|
}
|
|
|
|
private function formatScalaInternoShort($scala, $interno): string
|
|
{
|
|
$s = trim((string) ($scala ?? ''));
|
|
$i = trim((string) ($interno ?? ''));
|
|
|
|
if ($s !== '') {
|
|
$s = preg_replace('/^S/i', '', $s);
|
|
}
|
|
if ($i !== '') {
|
|
$i = preg_replace('/^I/i', '', $i);
|
|
}
|
|
|
|
if ($s !== '' && $i !== '') {
|
|
return $s . '-' . $i;
|
|
}
|
|
if ($s !== '') {
|
|
return $s;
|
|
}
|
|
if ($i !== '') {
|
|
return $i;
|
|
}
|
|
|
|
return '—';
|
|
}
|
|
|
|
public function openDettPersModal(int $nSpe): void
|
|
{
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
|
|
$operazione = DB::connection('gescon_import')
|
|
->table('operazioni')
|
|
->where('n_spe', $nSpe)
|
|
->whereIn('cod_spe', ['I05', 'I06'])
|
|
->when(
|
|
$legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile'),
|
|
fn($q) => $q->where('cod_stabile', $legacyCode)
|
|
)
|
|
->select(['importo_euro', 'importo', 'dt_spe', 'unico', 'gestione', 'n_stra'])
|
|
->first();
|
|
|
|
$dettCols = ['id_cond', 'cond_inq', 'importo', 'importo_euro'];
|
|
foreach (['unico', 'n_spe', 'n_stra', 'tipo_gestione', 'legacy_year'] as $optionalCol) {
|
|
if (Schema::connection('gescon_import')->hasColumn('dett_pers', $optionalCol)) {
|
|
$dettCols[] = $optionalCol;
|
|
}
|
|
}
|
|
|
|
$dettQuery = DB::connection('gescon_import')
|
|
->table('dett_pers');
|
|
|
|
$hasNspe = Schema::connection('gescon_import')->hasColumn('dett_pers', 'n_spe');
|
|
$hasNstra = Schema::connection('gescon_import')->hasColumn('dett_pers', 'n_stra');
|
|
$hasUnico = Schema::connection('gescon_import')->hasColumn('dett_pers', 'unico');
|
|
|
|
if ($hasNspe || $hasNstra || ($hasUnico && ! empty($operazione?->unico))) {
|
|
$dettQuery->where(function ($q) use ($nSpe, $operazione, $hasNspe, $hasNstra, $hasUnico): void {
|
|
if ($hasNspe) {
|
|
$q->orWhere('n_spe', $nSpe);
|
|
}
|
|
|
|
if ($hasNstra) {
|
|
$q->orWhere('n_stra', $nSpe);
|
|
}
|
|
|
|
if ($hasUnico && ! empty($operazione?->unico)) {
|
|
$q->orWhere('unico', $operazione->unico);
|
|
}
|
|
});
|
|
} else {
|
|
$dettQuery->whereRaw('1 = 0');
|
|
}
|
|
|
|
if (Schema::connection('gescon_import')->hasColumn('dett_pers', 'tipo_gestione') && ! empty($operazione?->gestione)) {
|
|
$dettQuery->where('tipo_gestione', $operazione->gestione);
|
|
}
|
|
|
|
if (Schema::connection('gescon_import')->hasColumn('dett_pers', 'legacy_year')) {
|
|
$dettQuery->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]);
|
|
}
|
|
|
|
$dettRows = $dettQuery->get($dettCols);
|
|
|
|
$this->dettPersNSpe = $nSpe;
|
|
$this->dettPersImportoOperazione = $this->normalizeMoney($operazione->importo_euro ?? $operazione->importo ?? 0);
|
|
$this->dettPersLegacyCode = $this->resolveLegacyCodiceStabile();
|
|
$this->dettPersLegacyYear = '0001';
|
|
|
|
$legacyAmounts = [];
|
|
$legacyUnico = [];
|
|
foreach ($dettRows as $dett) {
|
|
$idCond = is_numeric($dett->id_cond ?? null) ? (int) $dett->id_cond : 0;
|
|
$role = strtoupper(trim((string) ($dett->cond_inq ?? 'C')));
|
|
$role = $role === 'I' ? 'I' : 'C';
|
|
if ($idCond <= 0) {
|
|
continue;
|
|
}
|
|
$key = $idCond . '|' . $role;
|
|
$legacyAmounts[$key] = ($legacyAmounts[$key] ?? 0.0) + $this->normalizeMoney($dett->importo_euro ?? $dett->importo ?? 0);
|
|
|
|
if (isset($dett->unico) && is_numeric($dett->unico)) {
|
|
$legacyUnico[(int) $dett->unico] = true;
|
|
}
|
|
}
|
|
$this->dettPersLegacyAmounts = $legacyAmounts;
|
|
$this->dettPersLegacyUnico = array_values(array_map('intval', array_keys($legacyUnico)));
|
|
sort($this->dettPersLegacyUnico);
|
|
|
|
$recipients = [];
|
|
$targetOptions = [];
|
|
|
|
$legacyCode = $this->dettPersLegacyCode;
|
|
if ($legacyCode) {
|
|
$condQuery = DB::connection('gescon_import')
|
|
->table('condomin')
|
|
->where('cod_stabile', $legacyCode);
|
|
|
|
$condColumns = ['cod_cond', 'scala', 'interno', 'piano', 'cognome', 'nome', 'denominazione', 'proprietario_denominazione', 'inquilino_denominazione', 'inquil_nome'];
|
|
if (Schema::connection('gescon_import')->hasColumn('condomin', 'legacy_year') && $this->dettPersLegacyYear) {
|
|
$condQuery->whereIn('legacy_year', [$this->dettPersLegacyYear, '0001']);
|
|
$condColumns[] = 'legacy_year';
|
|
}
|
|
|
|
$condRows = $condQuery->get($condColumns);
|
|
|
|
$byId = [];
|
|
foreach ($condRows as $cond) {
|
|
$idCond = is_numeric($cond->cod_cond ?? null) ? (int) $cond->cod_cond : null;
|
|
if (! $idCond || isset($byId[$idCond])) {
|
|
continue;
|
|
}
|
|
$byId[$idCond] = $cond;
|
|
}
|
|
|
|
foreach ($byId as $idCond => $cond) {
|
|
$unita = trim(implode(' ', array_filter([
|
|
! empty($cond->scala) ? 'Scala ' . $cond->scala : null,
|
|
! empty($cond->interno) ? 'Int. ' . $cond->interno : null,
|
|
! empty($cond->piano) ? 'P. ' . $cond->piano : null,
|
|
])));
|
|
|
|
$propName = trim((string) ($cond->proprietario_denominazione ?? ''));
|
|
if ($propName === '') {
|
|
$propName = trim((string) (($cond->cognome ?? '') . ' ' . ($cond->nome ?? '')));
|
|
}
|
|
if ($propName === '') {
|
|
$propName = trim((string) ($cond->denominazione ?? ''));
|
|
}
|
|
|
|
$inquName = trim((string) ($cond->inquilino_denominazione ?? ''));
|
|
if ($inquName === '') {
|
|
$inquName = trim((string) ($cond->inquil_nome ?? ''));
|
|
}
|
|
|
|
$keyC = $idCond . '|C';
|
|
$recipients[$keyC] = [
|
|
'key' => $keyC,
|
|
'id_cond' => $idCond,
|
|
'cond_inq' => 'C',
|
|
'soggetto' => 'Condomino: ' . ($propName !== '' ? $propName : ('Cond. ' . $idCond)),
|
|
'unita' => $unita !== '' ? $unita : '—',
|
|
];
|
|
$targetOptions[$keyC] = ($unita !== '' ? ($unita . ' · ') : '') . 'Condomino ' . ($propName !== '' ? $propName : ('Cond. ' . $idCond));
|
|
|
|
if ($inquName !== '') {
|
|
$keyI = $idCond . '|I';
|
|
$recipients[$keyI] = [
|
|
'key' => $keyI,
|
|
'id_cond' => $idCond,
|
|
'cond_inq' => 'I',
|
|
'soggetto' => 'Inquilino: ' . $inquName,
|
|
'unita' => $unita !== '' ? $unita : '—',
|
|
];
|
|
$targetOptions[$keyI] = ($unita !== '' ? ($unita . ' · ') : '') . 'Inquilino ' . $inquName;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($legacyAmounts as $key => $amount) {
|
|
if (isset($recipients[$key])) {
|
|
continue;
|
|
}
|
|
|
|
[$idCond, $role] = array_pad(explode('|', $key), 2, 'C');
|
|
$idCond = is_numeric($idCond) ? (int) $idCond : 0;
|
|
$role = strtoupper($role) === 'I' ? 'I' : 'C';
|
|
|
|
$recipients[$key] = [
|
|
'key' => $key,
|
|
'id_cond' => $idCond,
|
|
'cond_inq' => $role,
|
|
'soggetto' => ($role === 'I' ? 'Inquilino' : 'Condomino') . ': Cond. ' . ($idCond > 0 ? $idCond : '—'),
|
|
'unita' => '—',
|
|
];
|
|
$targetOptions[$key] = 'Cond. ' . ($idCond > 0 ? $idCond : '—') . ' (' . ($role === 'I' ? 'Inquilino' : 'Condomino') . ')';
|
|
}
|
|
|
|
ksort($recipients);
|
|
$this->dettPersRecipients = array_values($recipients);
|
|
$this->dettPersRipartoTargetOptions = $targetOptions;
|
|
$this->dettPersRipartoTarget = array_key_first($targetOptions);
|
|
|
|
$tabOptions = [];
|
|
$activeStabileId = StabileContext::resolveActiveStabileId(Auth::user());
|
|
if ($activeStabileId && Schema::hasTable('tabelle_millesimali') && Schema::hasTable('dettaglio_millesimi')) {
|
|
$localRows = DB::table('tabelle_millesimali')
|
|
->where('stabile_id', (int) $activeStabileId)
|
|
->orderBy('codice_tabella')
|
|
->get(['id', 'codice_tabella', 'nome_tabella', 'denominazione']);
|
|
|
|
foreach ($localRows as $localRow) {
|
|
$key = 'LOCAL:' . (int) $localRow->id;
|
|
$label = trim((string) ($localRow->codice_tabella ?? 'TAB'));
|
|
$name = trim((string) ($localRow->denominazione ?? $localRow->nome_tabella ?? ''));
|
|
if ($name !== '') {
|
|
$label .= ' · ' . $name;
|
|
}
|
|
|
|
$sum = (float) DB::table('dettaglio_millesimi')
|
|
->where('tabella_millesimale_id', (int) $localRow->id)
|
|
->where('partecipa', true)
|
|
->sum('millesimi');
|
|
|
|
$tabOptions[$key] = $label . ' (totale ' . number_format($sum, 3, ',', '.') . ')';
|
|
}
|
|
}
|
|
|
|
if ($legacyCode && Schema::connection('gescon_import')->hasTable('dett_tab')) {
|
|
$tabQuery = DB::connection('gescon_import')
|
|
->table('dett_tab')
|
|
->where('cod_stabile', $legacyCode)
|
|
->select('cod_tab')
|
|
->distinct();
|
|
|
|
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
|
|
$tabQuery->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]);
|
|
}
|
|
|
|
$tabs = $tabQuery->orderBy('cod_tab')->pluck('cod_tab')->all();
|
|
foreach ($tabs as $tab) {
|
|
$code = strtoupper(trim((string) $tab));
|
|
if ($code === '') {
|
|
continue;
|
|
}
|
|
$tabOptions[$code] = ($tabOptions[$code] ?? $code);
|
|
}
|
|
}
|
|
|
|
$this->dettPersRipartoTabellaOptions = $tabOptions;
|
|
$this->dettPersRipartoTabella = array_key_first($tabOptions);
|
|
$this->dettPersRipartoMode = 'legacy';
|
|
|
|
$this->rebuildDettPersRows();
|
|
}
|
|
|
|
public function updatedDettPersRipartoMode(): void
|
|
{
|
|
$this->rebuildDettPersRows();
|
|
}
|
|
|
|
public function updatedDettPersRipartoTabella(): void
|
|
{
|
|
$this->rebuildDettPersRows();
|
|
}
|
|
|
|
public function updatedDettPersRipartoTarget(): void
|
|
{
|
|
$this->rebuildDettPersRows();
|
|
}
|
|
|
|
public function updatedDettPersRows($value = null, $key = null): void
|
|
{
|
|
if ($this->dettPersRipartoMode !== 'riparto_libero') {
|
|
return;
|
|
}
|
|
|
|
$this->recalculateDettPersTotalsFromRows();
|
|
}
|
|
|
|
private function rebuildDettPersRows(): void
|
|
{
|
|
$recipients = $this->dettPersRecipients;
|
|
$total = $this->dettPersImportoOperazione;
|
|
$amounts = [];
|
|
|
|
foreach ($recipients as $recipient) {
|
|
$key = (string) ($recipient['key'] ?? '');
|
|
if ($key !== '') {
|
|
$amounts[$key] = 0.0;
|
|
}
|
|
}
|
|
|
|
$mode = $this->dettPersRipartoMode;
|
|
if ($mode === 'parti_uguali') {
|
|
$keys = array_keys($amounts);
|
|
$distributed = $this->distributeEqually($total, $keys);
|
|
foreach ($distributed as $key => $value) {
|
|
$amounts[$key] = $value;
|
|
}
|
|
} elseif ($mode === 'riparto_libero') {
|
|
foreach ($this->dettPersRows as $row) {
|
|
$idCond = is_numeric($row['id_cond'] ?? null) ? (int) $row['id_cond'] : 0;
|
|
$role = strtoupper((string) ($row['cond_inq'] ?? 'C')) === 'I' ? 'I' : 'C';
|
|
if ($idCond <= 0) {
|
|
continue;
|
|
}
|
|
$key = $idCond . '|' . $role;
|
|
if (! array_key_exists($key, $amounts)) {
|
|
continue;
|
|
}
|
|
$amounts[$key] = round($this->normalizeMoney($row['importo'] ?? 0), 2);
|
|
}
|
|
} elseif ($mode === 'unita_singola') {
|
|
$target = (string) ($this->dettPersRipartoTarget ?? '');
|
|
if ($target !== '' && array_key_exists($target, $amounts)) {
|
|
$amounts[$target] = round($total, 2);
|
|
}
|
|
} elseif ($mode === 'millesimi_tabella') {
|
|
$weights = $this->resolveMillesimiWeights();
|
|
if (! empty($weights)) {
|
|
$distributed = $this->distributeByWeights($total, $weights);
|
|
foreach ($distributed as $key => $value) {
|
|
if (array_key_exists($key, $amounts)) {
|
|
$amounts[$key] = $value;
|
|
}
|
|
}
|
|
} else {
|
|
foreach ($this->dettPersLegacyAmounts as $key => $value) {
|
|
if (array_key_exists($key, $amounts)) {
|
|
$amounts[$key] = round((float) $value, 2);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
foreach ($this->dettPersLegacyAmounts as $key => $value) {
|
|
if (array_key_exists($key, $amounts)) {
|
|
$amounts[$key] = round((float) $value, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
$rows = [];
|
|
$totale = 0.0;
|
|
foreach ($recipients as $recipient) {
|
|
$key = (string) ($recipient['key'] ?? '');
|
|
$value = round((float) ($amounts[$key] ?? 0), 2);
|
|
$totale += $value;
|
|
|
|
$rows[] = [
|
|
'id_cond' => $recipient['id_cond'] ?? null,
|
|
'soggetto' => $recipient['soggetto'] ?? '—',
|
|
'unita' => $recipient['unita'] ?? '—',
|
|
'cond_inq' => $recipient['cond_inq'] ?? 'C',
|
|
'importo' => $value,
|
|
];
|
|
}
|
|
|
|
$this->dettPersRows = $rows;
|
|
$this->dettPersTotale = round($totale, 2);
|
|
$this->dettPersDelta = round($this->dettPersImportoOperazione - $this->dettPersTotale, 2);
|
|
$this->dettPersQuadraturaOk = abs($this->dettPersDelta) <= 0.01;
|
|
}
|
|
|
|
private function distributeEqually(float $total, array $keys): array
|
|
{
|
|
$out = [];
|
|
if (empty($keys)) {
|
|
return $out;
|
|
}
|
|
|
|
$count = count($keys);
|
|
$base = round($total / $count, 2);
|
|
foreach ($keys as $key) {
|
|
$out[$key] = $base;
|
|
}
|
|
|
|
$sum = array_sum($out);
|
|
$delta = round($total - $sum, 2);
|
|
if ($delta !== 0.0) {
|
|
$firstKey = $keys[0];
|
|
$out[$firstKey] = round(($out[$firstKey] ?? 0) + $delta, 2);
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
private function distributeByWeights(float $total, array $weights): array
|
|
{
|
|
$sumWeights = array_sum($weights);
|
|
if ($sumWeights <= 0) {
|
|
return [];
|
|
}
|
|
|
|
$out = [];
|
|
foreach ($weights as $key => $weight) {
|
|
$share = ($total * ((float) $weight / $sumWeights));
|
|
$out[$key] = round($share, 2);
|
|
}
|
|
|
|
$sum = array_sum($out);
|
|
$delta = round($total - $sum, 2);
|
|
if ($delta !== 0.0) {
|
|
$firstKey = array_key_first($out);
|
|
if ($firstKey !== null) {
|
|
$out[$firstKey] = round(($out[$firstKey] ?? 0) + $delta, 2);
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
private function resolveMillesimiWeights(): array
|
|
{
|
|
$tabKey = trim((string) ($this->dettPersRipartoTabella ?? ''));
|
|
if ($tabKey === '') {
|
|
return [];
|
|
}
|
|
|
|
if (str_starts_with($tabKey, 'LOCAL:') && Schema::hasTable('dettaglio_millesimi') && Schema::hasTable('unita_immobiliari')) {
|
|
$tabellaId = (int) str_replace('LOCAL:', '', $tabKey);
|
|
if ($tabellaId <= 0) {
|
|
return [];
|
|
}
|
|
|
|
$rows = DB::table('dettaglio_millesimi as dm')
|
|
->join('unita_immobiliari as ui', 'ui.id', '=', 'dm.unita_immobiliare_id')
|
|
->where('dm.tabella_millesimale_id', $tabellaId)
|
|
->where('dm.partecipa', true)
|
|
->get(['ui.legacy_cond_id', 'dm.millesimi']);
|
|
|
|
$weights = [];
|
|
foreach ($rows as $row) {
|
|
$idCond = is_numeric($row->legacy_cond_id ?? null) ? (int) $row->legacy_cond_id : 0;
|
|
if ($idCond <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$w = $this->normalizeMoney($row->millesimi ?? 0);
|
|
if ($w <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$key = $idCond . '|C';
|
|
$weights[$key] = ($weights[$key] ?? 0.0) + $w;
|
|
}
|
|
|
|
return $weights;
|
|
}
|
|
|
|
$legacyCode = $this->dettPersLegacyCode;
|
|
$codTab = strtoupper($tabKey);
|
|
if (! $legacyCode || ! Schema::connection('gescon_import')->hasTable('dett_tab')) {
|
|
return [];
|
|
}
|
|
|
|
$query = DB::connection('gescon_import')
|
|
->table('dett_tab')
|
|
->where('cod_stabile', $legacyCode)
|
|
->where('cod_tab', $codTab);
|
|
|
|
if (Schema::connection('gescon_import')->hasColumn('dett_tab', 'legacy_year')) {
|
|
$query->whereIn('legacy_year', ['0001', (string) \Carbon\Carbon::now()->format('Y')]);
|
|
}
|
|
|
|
$rows = $query->get(['id_cond', 'cond_inquil', 'cons_euro', 'mm', 'prev_euro']);
|
|
$weights = [];
|
|
foreach ($rows as $row) {
|
|
$idCond = is_numeric($row->id_cond ?? null) ? (int) $row->id_cond : 0;
|
|
$role = strtoupper(trim((string) ($row->cond_inquil ?? 'C')));
|
|
$role = $role === 'I' ? 'I' : 'C';
|
|
if ($idCond <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$weight = $this->normalizeMoney($row->cons_euro ?? 0);
|
|
if ($weight <= 0) {
|
|
$weight = $this->normalizeMoney($row->mm ?? 0);
|
|
}
|
|
if ($weight <= 0) {
|
|
$weight = $this->normalizeMoney($row->prev_euro ?? 0);
|
|
}
|
|
if ($weight <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$key = $idCond . '|' . $role;
|
|
$weights[$key] = ($weights[$key] ?? 0.0) + $weight;
|
|
}
|
|
|
|
return $weights;
|
|
}
|
|
|
|
private function recalculateDettPersTotalsFromRows(): void
|
|
{
|
|
$totale = 0.0;
|
|
foreach ($this->dettPersRows as $idx => $row) {
|
|
$value = round($this->normalizeMoney($row['importo'] ?? 0), 2);
|
|
$this->dettPersRows[$idx]['importo'] = $value;
|
|
$totale += $value;
|
|
}
|
|
|
|
$this->dettPersTotale = round($totale, 2);
|
|
$this->dettPersDelta = round($this->dettPersImportoOperazione - $this->dettPersTotale, 2);
|
|
$this->dettPersQuadraturaOk = abs($this->dettPersDelta) <= 0.01;
|
|
}
|
|
|
|
private function normalizeMoney($value): float
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return 0.0;
|
|
}
|
|
$s = trim((string) $value);
|
|
$s = str_replace([' ', "\r", "\n", "\t"], '', $s);
|
|
if (str_contains($s, ',') && str_contains($s, '.')) {
|
|
$s = str_replace('.', '', $s);
|
|
$s = str_replace(',', '.', $s);
|
|
} elseif (str_contains($s, ',')) {
|
|
$s = str_replace(',', '.', $s);
|
|
}
|
|
return is_numeric($s) ? (float) $s : 0.0;
|
|
}
|
|
|
|
private function normalizeInvoiceNumber($value): ?string
|
|
{
|
|
$raw = strtoupper(trim((string) ($value ?? '')));
|
|
if ($raw === '') {
|
|
return null;
|
|
}
|
|
|
|
$normalized = preg_replace('/[^A-Z0-9]/', '', $raw);
|
|
if (! is_string($normalized) || $normalized === '') {
|
|
return null;
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
private function computeRdaDueDate(?string $paymentDate): ?string
|
|
{
|
|
if (! $paymentDate) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$due = \Carbon\Carbon::parse($paymentDate)
|
|
->startOfMonth()
|
|
->addMonthNoOverflow()
|
|
->day(16)
|
|
->startOfDay();
|
|
|
|
while ($this->isNonWorkingDay($due)) {
|
|
$due->addDay();
|
|
}
|
|
|
|
return $due->format('Y-m-d');
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function isNonWorkingDay(\Carbon\Carbon $day): bool
|
|
{
|
|
if ($day->isWeekend()) {
|
|
return true;
|
|
}
|
|
|
|
$fixed = [
|
|
'01-01',
|
|
'01-06',
|
|
'04-25',
|
|
'05-01',
|
|
'06-02',
|
|
'08-15',
|
|
'11-01',
|
|
'12-08',
|
|
'12-25',
|
|
'12-26',
|
|
];
|
|
|
|
if (in_array($day->format('m-d'), $fixed, true)) {
|
|
return true;
|
|
}
|
|
|
|
$year = (int) $day->format('Y');
|
|
$easter = \Carbon\Carbon::createFromTimestamp(easter_date($year))->startOfDay();
|
|
$easterMonday = $easter->copy()->addDay();
|
|
|
|
return $day->isSameDay($easter) || $day->isSameDay($easterMonday);
|
|
}
|
|
|
|
private function buildOperazioniQuery()
|
|
{
|
|
$query = DB::connection('gescon_import')->table('operazioni');
|
|
|
|
$legacyCode = $this->resolveLegacyCodiceStabile();
|
|
if ($legacyCode && Schema::connection('gescon_import')->hasColumn('operazioni', 'cod_stabile')) {
|
|
$query->where('cod_stabile', $legacyCode);
|
|
}
|
|
|
|
if ($this->onlyStraordinarieMode) {
|
|
$query->where('gestione', 'S');
|
|
|
|
if ($this->selectedStraordinaria !== null) {
|
|
$query->where('n_stra', $this->selectedStraordinaria);
|
|
}
|
|
} elseif ($this->netgesconWorkflowMode) {
|
|
$query->where(function ($q) {
|
|
$q->where('gestione', 'O')
|
|
->orWhereNull('gestione')
|
|
->orWhere('gestione', '');
|
|
});
|
|
}
|
|
|
|
if ($this->search) {
|
|
$s = '%' . $this->search . '%';
|
|
$query->where(function ($q) use ($s) {
|
|
$q->where('cod_for', 'like', $s)
|
|
->orWhere('cod_spe', 'like', $s)
|
|
->orWhere('benef', 'like', $s)
|
|
->orWhere('num_fat', 'like', $s)
|
|
->orWhere('fe_uid', 'like', $s)
|
|
->orWhere('rif_rda', 'like', $s)
|
|
->orWhere('id_operaz', 'like', $s)
|
|
->orWhere('n_spe', 'like', $s)
|
|
->orWhere('n_stra', 'like', $s);
|
|
});
|
|
}
|
|
|
|
if ($this->filterCodForn) {
|
|
$query->where('cod_for', $this->filterCodForn);
|
|
}
|
|
if ($this->filterCodSpe) {
|
|
$query->where('cod_spe', $this->filterCodSpe);
|
|
}
|
|
if ($this->filterTabella) {
|
|
$query->where('tabella', $this->filterTabella);
|
|
}
|
|
if ($this->filterBenef) {
|
|
$query->where('benef', 'like', '%' . $this->filterBenef . '%');
|
|
}
|
|
if ($this->filterRdaOnly) {
|
|
$query->where(function ($q) {
|
|
$q->whereNotNull('rif_rda')
|
|
->orWhere('benef', 'like', 'RDA:%');
|
|
});
|
|
}
|
|
if ($this->filterAnno) {
|
|
$query->whereYear('dt_spe', $this->filterAnno);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
private function computeSignedAmount($row): float
|
|
{
|
|
$base = $this->normalizeMoney($row->importo_euro ?? $row->importo ?? 0);
|
|
$spese = $this->normalizeMoney($row->importo_spese ?? 0);
|
|
$entrate = $this->normalizeMoney($row->importo_entrate ?? 0);
|
|
$debiti = $this->normalizeMoney($row->importo_debiti ?? 0);
|
|
$crediti = $this->normalizeMoney($row->importo_crediti ?? 0);
|
|
|
|
if (($spese + $entrate + $debiti + $crediti) > 0) {
|
|
return ($spese + $debiti) - ($entrate + $crediti);
|
|
}
|
|
|
|
$natura = strtoupper(trim((string) ($row->natura2 ?? '')));
|
|
if ($natura !== '' && in_array($natura, ['E', 'ENTRATA', 'ENTRATE', 'CREDITO'], true)) {
|
|
return -1 * $base;
|
|
}
|
|
|
|
if (strtoupper((string) ($row->cod_spe ?? '')) === 'RB1') {
|
|
return -1 * $base;
|
|
}
|
|
|
|
return $base;
|
|
}
|
|
|
|
public function openMastrino(string $tabella, string $codSpe): void
|
|
{
|
|
$this->mastrinoTabella = $tabella;
|
|
$this->mastrinoCodSpe = $codSpe;
|
|
|
|
$query = $this->buildOperazioniQuery()
|
|
->where('cod_spe', $codSpe)
|
|
->orderBy('dt_spe')
|
|
->orderBy('id_operaz');
|
|
|
|
if (trim($tabella) !== '') {
|
|
$query->where('tabella', $tabella);
|
|
}
|
|
|
|
if ($this->filterAnno) {
|
|
$query->whereYear('dt_spe', $this->filterAnno);
|
|
}
|
|
|
|
$rows = $query->get();
|
|
|
|
$this->mastrinoRows = $rows->map(function ($row) {
|
|
return [
|
|
'id' => $row->id_operaz ?? null,
|
|
'data' => $row->dt_spe ?? null,
|
|
'descrizione' => $row->des_oper ?? $row->benef ?? null,
|
|
'importo' => $this->computeSignedAmount($row),
|
|
'n_spe' => $row->n_spe ?? null,
|
|
'n_stra' => $row->n_stra ?? null,
|
|
'tabella' => $row->tabella ?? null,
|
|
'fe_uid' => $row->fe_uid ?? null,
|
|
'rif_rda' => $row->rif_rda ?? null,
|
|
'ra_importo' => $row->netto_vers_rda ?? $row->importo_euro_770 ?? null,
|
|
];
|
|
})->all();
|
|
}
|
|
|
|
public function sortBy(string $field): void
|
|
{
|
|
$allowed = [
|
|
'id_operaz',
|
|
'dt_spe',
|
|
'cod_spe',
|
|
'tabella',
|
|
'cod_for',
|
|
'benef',
|
|
'importo_euro',
|
|
'num_fat',
|
|
'fe_uid',
|
|
'rif_rda',
|
|
];
|
|
|
|
if (! in_array($field, $allowed, true)) {
|
|
return;
|
|
}
|
|
|
|
if ($this->sortField === $field) {
|
|
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
|
} else {
|
|
$this->sortField = $field;
|
|
$this->sortDirection = 'asc';
|
|
}
|
|
}
|
|
|
|
public function getSpecialCodesSummaryProperty(): array
|
|
{
|
|
$base = $this->buildOperazioniQuery();
|
|
|
|
$rows = $base
|
|
->whereIn('cod_spe', ['Z01', 'Z02'])
|
|
->select('cod_spe')
|
|
->selectRaw("SUM(
|
|
CASE
|
|
WHEN (COALESCE(importo_spese, 0) + COALESCE(importo_entrate, 0) + COALESCE(importo_debiti, 0) + COALESCE(importo_crediti, 0)) = 0 THEN
|
|
CASE
|
|
WHEN natura2 IN ('E', 'ENTRATA', 'ENTRATE', 'CREDITO') THEN -1 * COALESCE(importo_euro, importo, 0)
|
|
ELSE COALESCE(importo_euro, importo, 0)
|
|
END
|
|
ELSE (COALESCE(importo_spese, 0) + COALESCE(importo_debiti, 0) - COALESCE(importo_entrate, 0) - COALESCE(importo_crediti, 0))
|
|
END
|
|
) as totale")
|
|
->groupBy('cod_spe')
|
|
->get();
|
|
|
|
$map = $rows->mapWithKeys(fn($row) => [strtoupper((string) $row->cod_spe) => (float) ($row->totale ?? 0)])->all();
|
|
|
|
return [
|
|
'Z01' => (float) ($map['Z01'] ?? 0),
|
|
'Z02' => (float) ($map['Z02'] ?? 0),
|
|
'totale' => (float) (($map['Z01'] ?? 0) + ($map['Z02'] ?? 0)),
|
|
];
|
|
}
|
|
|
|
private function applyOperazioniSorting($query)
|
|
{
|
|
$dir = strtolower($this->sortDirection) === 'asc' ? 'asc' : 'desc';
|
|
$field = $this->sortField;
|
|
|
|
switch ($field) {
|
|
case 'importo_euro':
|
|
$query->orderByRaw('COALESCE(importo_euro, importo, 0) ' . $dir);
|
|
break;
|
|
default:
|
|
$query->orderBy($field, $dir);
|
|
break;
|
|
}
|
|
|
|
if ($field !== 'id_operaz') {
|
|
$query->orderBy('id_operaz', 'desc');
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
}
|