netgescon-day0/tests/Feature/GermanicoBancaReconciliationTest.php

205 lines
7.8 KiB
PHP

<?php
use App\Filament\Pages\Gescon\Ordinarie;
use App\Filament\Pages\UnitaImmobiliarePage;
use App\Models\Stabile;
use App\Models\UnitaImmobiliare;
use App\Models\User;
use App\Services\Contabilita\GermanicoBancaReconciliationService;
use App\Services\Gescon\GesconEstrattoContoService;
use App\Support\StabileContext;
use Illuminate\Support\Facades\DB;
use Livewire\Livewire;
beforeEach(function () {
DB::table('amministratori')->insertOrIgnore([
'id' => 1,
'nome' => 'Admin',
'cognome' => 'Test',
'created_at' => now(),
'updated_at' => now(),
]);
$this->user = User::factory()->create();
if (method_exists($this->user, 'assignRole')) {
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);
$this->user->assignRole('admin');
}
DB::table('stabili')->insertOrIgnore([
'id' => 19,
'amministratore_id' => 1,
'codice_stabile' => '0016',
'denominazione' => 'CONDOMINIO VIA GERMANICO 96',
'indirizzo' => 'Via Germanico 96',
'citta' => 'Roma',
'cap' => '00192',
'provincia' => 'RM',
'attivo' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
$this->stabile = Stabile::find(19);
DB::table('unita_immobiliari')->insertOrIgnore([
'id' => 321,
'stabile_id' => 19,
'scala' => 'B',
'interno' => '4',
'codice_unita' => '0016-B-4',
'denominazione' => 'Barone Michele',
'legacy_cond_id' => '47',
'created_at' => now(),
'updated_at' => now(),
]);
$this->unita = UnitaImmobiliare::find(321);
DB::table('unita_immobiliare_nominativi')->insertOrIgnore([
[
'unita_immobiliare_id' => 321,
'nominativo' => 'Barone Michele',
'ruolo' => 'P',
'created_at' => now(),
'updated_at' => now(),
],
[
'unita_immobiliare_id' => 321,
'nominativo' => 'Francioni Claudia',
'ruolo' => 'CP',
'created_at' => now(),
'updated_at' => now(),
],
]);
StabileContext::setActiveStabileId($this->user, 19);
});
test('unit 321 owner picker includes barone michele and francioni claudia', function () {
$component = Livewire::actingAs($this->user)
->withQueryParams(['unita_id' => 321])
->test(UnitaImmobiliarePage::class, ['unita_id' => 321]);
$unitaOptions = $component->instance()->getUnitaOptions();
expect($unitaOptions)->toBeArray()
->and(isset($unitaOptions[321]))->toBeTrue();
$label321 = $unitaOptions[321];
expect($label321)->toContain('Barone Michele')
->and($label321)->toContain('Scala B')
->and($label321)->toContain('Int. 4');
});
test('bank reconciliation service reconciles over 85 percent of germanico bank transactions', function () {
$service = app(GermanicoBancaReconciliationService::class);
$result = $service->syncAndReconcileAll();
expect($result)->toBeArray()
->and($result['total_movimenti'])->toBeGreaterThanOrEqual(1900)
->and($result['totale_riconciliati'])->toBeGreaterThanOrEqual(1600)
->and($result['percentuale'])->toBeGreaterThanOrEqual(85.0);
// Verifica che l'unita 321 abbia transazioni riconciliate
$unitaMovs = $service->getMovimentiForUnita(321);
expect($unitaMovs->count())->toBeGreaterThanOrEqual(7);
// Verifica bonifico del 23/12/2025 di 960,79 euro
$bonifico960 = $unitaMovs->first(fn($m) => abs((float)$m->importo - 960.79) < 0.01);
expect($bonifico960)->not->toBeNull()
->and($bonifico960->stato_riconciliazione)->toBe('riconciliato');
$matchData = is_string($bonifico960->match_data) ? json_decode($bonifico960->match_data, true) : (array) $bonifico960->match_data;
expect($matchData['pdf_ricevuta'])->toBe('Inc_EC_20260310_184901.pdf');
});
test('gescon estratto conto service computes exact benchmark 885.00 for germanico unit 321', function () {
// Esegue sync/reconcile per popolare i movimenti bancari
$bankService = app(GermanicoBancaReconciliationService::class);
$bankService->syncAndReconcileAll();
$unita = UnitaImmobiliare::find(321);
expect($unita)->not->toBeNull();
$service = app(GesconEstrattoContoService::class);
$ec = $service->getEstrattoConto($unita, 'C', true);
expect($ec)->toBeArray()
->and($ec['codice_stabile'])->toBe('0016')
->and($ec['totali']['totale_dovuto'])->toBe(885.00)
->and($ec['totali']['totale_ordinaria_corrente_residuo'])->toBe(885.00)
->and($ec['totali']['totale_ordinarie_pregresse_residuo'])->toBe(0.0)
->and($ec['totali']['totale_straordinarie_residuo'])->toBe(0.0);
// Verifica ordinaria corrente 2025/26: 6 rate da 177.00 di cui rata 1 saldata
$ord = $ec['gestione_ordinaria_corrente'];
expect($ord['totale_dovuto'])->toBe(1062.00)
->and($ord['totale_pagato'])->toBe(177.00)
->and($ord['residuo'])->toBe(885.00)
->and(count($ord['rate']))->toBe(6);
$rata1 = $ord['rate'][0];
expect($rata1['stato'])->toBe('Saldata')
->and($rata1['pagato'])->toBe(177.00)
->and($rata1['residuo'])->toBe(0.0)
->and($rata1['data_pagamento'])->toBe('23/12/2025')
->and($rata1['movimento_bancario'])->not->toBeNull()
->and($rata1['movimento_bancario']['importo'])->toBe(960.79)
->and($rata1['ricevuta_pdf'])->toBe('Inc_EC_20260310_184901.pdf');
// Verifica movimenti bancari riconciliati allegati all'unita
expect($ec['movimenti_bancari'])->toBeArray()
->and(count($ec['movimenti_bancari']))->toBeGreaterThanOrEqual(7);
$mov960 = collect($ec['movimenti_bancari'])->first(fn($m) => abs($m['importo'] - 960.79) < 0.01);
expect($mov960)->not->toBeNull()
->and($mov960['stato'])->toBe('riconciliato')
->and($mov960['pdf_ricevuta'])->toBe('Inc_EC_20260310_184901.pdf');
});
test('ordinarie page renders banca tab and matches incassi from singolo_anno mdb', function () {
$bankService = app(GermanicoBancaReconciliationService::class);
$bankService->syncAndReconcileAll();
auth()->login($this->user);
$page = new Ordinarie();
$page->mount();
// Test KPI banca
$kpi = $page->bancaKpi;
expect($kpi)->toBeArray()
->and($kpi['total'])->toBeGreaterThanOrEqual(1900)
->and($kpi['riconciliati'])->toBeGreaterThanOrEqual(1600)
->and($kpi['iban'])->toBe(GermanicoBancaReconciliationService::IBAN_GERMANICO);
// Test ricerca movimento Barone Michele
$page->bancaSearch = 'Barone Michele';
$movimenti = $page->movimentiBanca;
expect($movimenti->total())->toBeGreaterThanOrEqual(7);
$firstMov = $movimenti->first();
expect($firstMov->estratto_conto_url)->toContain('unita-immobiliare?unita_id=321')
->and($firstMov->match_pdf)->toContain('.pdf');
// Test incassi da MDB con arricchimento bancario
$page->search = null;
$incassi = $page->incassi;
expect($incassi)->not->toBeNull()
->and($incassi->total())->toBeGreaterThan(0);
// Verifica che gli incassi abbiano il matching bancario se presente
$matched = collect($incassi->items())->filter(fn($i) => !empty($i->movimento_banca));
expect($matched->count())->toBeGreaterThan(0);
});
test('receipt pdf route streams valid pdf document for inc_ec', function () {
$response = $this->actingAs($this->user)
->get(route('admin.gescon.inc_ec.pdf', [
'stabile' => '0016',
'file' => 'Inc_EC_20260310_184901.pdf',
]));
$response->assertStatus(200);
$response->assertHeader('content-type', 'application/pdf');
});