337 lines
13 KiB
PHP
337 lines
13 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(),
|
|
],
|
|
[
|
|
'id' => 287,
|
|
'stabile_id' => 19,
|
|
'scala' => 'A',
|
|
'interno' => '13',
|
|
'codice_unita' => '0016-A-13',
|
|
'denominazione' => 'Lucaferri Antonella',
|
|
'legacy_cond_id' => '13',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
],
|
|
[
|
|
'id' => 295,
|
|
'stabile_id' => 19,
|
|
'scala' => 'A',
|
|
'interno' => '21',
|
|
'codice_unita' => '0016-A-21',
|
|
'denominazione' => 'Volpetti Elisabetta',
|
|
'legacy_cond_id' => '21',
|
|
'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');
|
|
});
|
|
|
|
test('unit 321 multicanale contacts displays real barone michele data and germanico address fallback', function () {
|
|
auth()->login($this->user);
|
|
StabileContext::setActiveStabileId($this->user, 19);
|
|
request()->merge(['unita_id' => 321]);
|
|
|
|
$page = new UnitaImmobiliarePage();
|
|
$page->mount();
|
|
|
|
$recapiti = $page->recapitiMulticanaleTable;
|
|
expect($recapiti)->toBeArray()->and(count($recapiti))->toBeGreaterThan(0);
|
|
|
|
$valori = collect($recapiti)->pluck('valore')->all();
|
|
$valoriStr = implode(' | ', $valori);
|
|
|
|
// Non deve mostrare i recapiti estranei di Skynet Solutions SRL
|
|
expect(str_contains($valoriStr, 'gmpskynet@gmail.com'))->toBeFalse();
|
|
expect(str_contains($valoriStr, '33583314342'))->toBeFalse();
|
|
expect(str_contains($valoriStr, 'Via Andrea Bafilr'))->toBeFalse();
|
|
|
|
// Deve mostrare i recapiti reali di Barone Michele / Cecilia Tordini
|
|
expect($valoriStr)->toContain('barone.michele@gmail.com')
|
|
->and($valoriStr)->toContain('3388155553');
|
|
|
|
// Indirizzo di residenza assente nel profilo cade sullo stabile
|
|
expect($valoriStr)->toContain('Via Germanico 96 00192 Roma (RM)');
|
|
});
|
|
|
|
test('gestione affitti page mounts and renders cleanly without 500 error', function () {
|
|
$component = Livewire::actingAs($this->user)
|
|
->test(\App\Filament\Pages\Affitti\GestioneAffitti::class);
|
|
|
|
$component->assertSuccessful();
|
|
expect($component->instance()->tabs)->toBeArray()
|
|
->and(count($component->instance()->tabs))->toBe(6);
|
|
});
|
|
|
|
test('electronic invoice fe 255 allows access to amministratore user', function () {
|
|
$adminUser = User::factory()->create();
|
|
\Spatie\Permission\Models\Role::firstOrCreate(['name' => 'amministratore', 'guard_name' => 'web']);
|
|
$adminUser->assignRole('amministratore');
|
|
|
|
auth()->login($adminUser);
|
|
|
|
expect(\App\Filament\Pages\Contabilita\FatturaElettronicaScheda::canAccess())->toBeTrue();
|
|
});
|
|
|
|
test('ordinarie page bank table links lucaferri a13 to unit 287 and unit 295 does not leak 0013 straordinarie', function () {
|
|
auth()->login($this->user);
|
|
StabileContext::setActiveStabileId($this->user, 19);
|
|
|
|
$page = new Ordinarie();
|
|
$page->mount();
|
|
$page->search = 'Lucaferri';
|
|
|
|
// Verifica matching incassi MDB: Lucaferri Antonella (A-13) punta all'unita 287
|
|
$itemA13 = $page->incassi->first();
|
|
expect($itemA13)->not->toBeNull();
|
|
expect(str_contains((string) $itemA13->nominativo_pagante, 'Lucaferri'))->toBeTrue();
|
|
expect(str_contains((string) $itemA13->unita_ec_url, 'unita_id=287'))->toBeTrue();
|
|
expect(str_contains((string) $itemA13->unita_ec_url, 'unita_id=295'))->toBeFalse();
|
|
|
|
// Verifica che l'estratto conto dell'unita 295 non contenga dati dello Stabile 0013 Spadavecchia
|
|
$unita295 = UnitaImmobiliare::findOrFail(295);
|
|
$service = app(GesconEstrattoContoService::class);
|
|
$ec295 = $service->getEstrattoConto($unita295, 'C', true);
|
|
|
|
expect($ec295['codice_stabile'])->toBe('0016');
|
|
|
|
$straordTitles = collect($ec295['gestioni_straordinarie'] ?? [])->pluck('titolo')->all();
|
|
$straordStr = implode(' | ', $straordTitles);
|
|
|
|
expect(str_contains($straordStr, 'PASSERELLA'))->toBeFalse();
|
|
expect(str_contains($straordStr, 'CORNICIONE'))->toBeFalse();
|
|
expect(str_contains($straordStr, 'SPADAVECCHIA'))->toBeFalse();
|
|
});
|
|
|
|
test('bank documents upload list and delete functionality in ordinarie', function () {
|
|
auth()->login($this->user);
|
|
StabileContext::setActiveStabileId($this->user, 19);
|
|
|
|
$page = new Ordinarie();
|
|
$page->mount();
|
|
|
|
$initialCount = $page->bankDocuments->count();
|
|
|
|
$fakeFile = \Illuminate\Http\UploadedFile::fake()->create('estratto_conto_fucino_2025.pdf', 120, 'application/pdf');
|
|
$page->bankDocumentTipo = 'estratto_conto';
|
|
$page->bankDocumentDescrizione = 'Estratto Conto Ufficiale Q4 2025';
|
|
$page->bankDocumentFile = $fakeFile;
|
|
|
|
$page->uploadBankDocument();
|
|
|
|
$docs = $page->getBankDocumentsProperty();
|
|
expect($docs->count())->toBe($initialCount + 1);
|
|
|
|
$uploaded = $docs->first();
|
|
expect($uploaded->nome_originale)->toBe('estratto_conto_fucino_2025.pdf')
|
|
->and($uploaded->categoria)->toBe('bancari')
|
|
->and($uploaded->stabile_id)->toBe(19);
|
|
|
|
// Test eliminazione documento
|
|
$page->deleteBankDocument($uploaded->id);
|
|
|
|
$docsAfterDelete = $page->getBankDocumentsProperty();
|
|
expect($docsAfterDelete->count())->toBe($initialCount);
|
|
});
|
|
|