# 🧮 TEST CONTABILITÀ - NetGesCon Laravel **📅 Creato**: 9 Luglio 2025 **🎯 Scopo**: Test specifici calcoli contabili e precisione **⚠️ Priorità**: **CRITICA** - Zero tolleranza errori **🔍 Focus**: Arrotondamenti, millesimi, quadrature --- ## 🚨 **PROBLEMI CRITICI DA RISOLVERE** ### ❌ **Problema Arrotondamenti** `[CRITICO]` ```php // ERRORE ATTUALE $totale = 1000.00; $parti = 3; $quota_singola = $totale / $parti; // 333.33333... $quota_arrotondata = round($quota_singola, 2); // 333.33 $totale_ricomposto = $quota_arrotondata * $parti; // 999.99 ❌ // RISULTATO: Perdita di 0.01€ per arrotondamento ``` ### ✅ **Soluzione Corretta** ```php // IMPLEMENTAZIONE CORRETTA public function distribuisciImporto($totale, $millesimi_array) { $totale_millesimi = array_sum($millesimi_array); $importi = []; $totale_assegnato = 0; foreach ($millesimi_array as $key => $millesimi) { if ($key === array_key_last($millesimi_array)) { // L'ultimo soggetto prende il resto $importi[$key] = $totale - $totale_assegnato; } else { $importo = round(($totale * $millesimi) / $totale_millesimi, 2); $importi[$key] = $importo; $totale_assegnato += $importo; } } return $importi; } // VERIFICA: array_sum($importi) === $totale SEMPRE ✅ ``` --- ## 🧪 **SUITE TEST CONTABILITÀ** ### 1️⃣ **Test Distribuzione Millesimi** #### **Test Case 1: Divisione Semplice** ```php public function test_distribuzione_millesimi_semplice() { // Setup $totale_spesa = 1200.00; $millesimi = [ 'unita_1' => 300, // 30% 'unita_2' => 400, // 40% 'unita_3' => 300 // 30% ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert $this->assertEquals(360.00, $distribuzione['unita_1']); // 30% di 1200 $this->assertEquals(480.00, $distribuzione['unita_2']); // 40% di 1200 $this->assertEquals(360.00, $distribuzione['unita_3']); // 30% di 1200 $this->assertEquals($totale_spesa, array_sum($distribuzione)); // ✅ CRITICO } ``` #### **Test Case 2: Divisione con Resto** ```php public function test_distribuzione_con_resto_non_divisibile() { // Setup - Caso problematico: 1000€ / 3 parti $totale_spesa = 1000.00; $millesimi = [ 'unita_1' => 333, // 33.3% 'unita_2' => 333, // 33.3% 'unita_3' => 334 // 33.4% (leggermente superiore) ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert - Il resto va all'ultimo $this->assertEquals(333.00, $distribuzione['unita_1']); $this->assertEquals(333.00, $distribuzione['unita_2']); $this->assertEquals(334.00, $distribuzione['unita_3']); // Prende il resto $this->assertEquals(1000.00, array_sum($distribuzione)); // ✅ QUADRATURA PERFETTA } ``` #### **Test Case 3: Millesimi Reali Condominio** ```php public function test_distribuzione_millesimi_reali() { // Setup - Dati reali da DATI_ESEMPIO.md $totale_spesa = 2847.50; // Spesa irregolare $millesimi = [ 'app_1' => 95, // App. 1 'app_2' => 85, // App. 2 'app_3' => 105, // App. 3 'app_4' => 90, // App. 4 'app_5' => 110, // App. 5 'app_6' => 95, // App. 6 'app_7' => 75, // App. 7 'app_8' => 70, // App. 8 'garage_1' => 18, 'garage_2' => 22 ]; $totale_millesimi = 765; // Parti private // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert $this->assertEquals($totale_spesa, array_sum($distribuzione), 'Quadratura totale'); $this->assertCount(count($millesimi), $distribuzione, 'Numero distribuzioni'); // Verifica proporzioni approssimative $proporzione_app1 = $distribuzione['app_1'] / $totale_spesa; $proporzione_attesa = 95 / $totale_millesimi; $this->assertEqualsWithDelta($proporzione_attesa, $proporzione_app1, 0.01, 'Proporzione corretta'); } ``` ### 2️⃣ **Test Calcoli Rate Condominiali** #### **Test Case 4: Calcolo Rate Trimestrali** ```php public function test_calcolo_rate_trimestrali() { // Setup $budget_annuale = 18000.00; $millesimi_unita = [ 'unita_1' => 95, 'unita_2' => 85, 'unita_3' => 105 ]; $totale_millesimi = 285; // Execute $rate_trimestrali = $this->contabilitaService->calcolaRateTrimestrali($budget_annuale, $millesimi_unita); // Assert foreach ($rate_trimestrali as $unita => $rata) { $rata_annuale = $rata * 4; $proporzione = $millesimi_unita[$unita] / $totale_millesimi; $importo_atteso = $budget_annuale * $proporzione; $this->assertEqualsWithDelta($importo_atteso, $rata_annuale, 0.04, "Rata annuale $unita"); } // Verifica quadratura totale $totale_rate_annuali = array_sum($rate_trimestrali) * 4; $this->assertEqualsWithDelta($budget_annuale, $totale_rate_annuali, 0.04, 'Budget quadrato'); } ``` ### 3️⃣ **Test Ripartizioni Speciali** #### **Test Case 5: Riscaldamento (Solo Appartamenti)** ```php public function test_ripartizione_riscaldamento_solo_appartamenti() { // Setup - Escludere garage/cantine $spesa_riscaldamento = 5400.00; $millesimi_riscaldamento = [ 'app_1' => 145, // Piano terra (maggiore dispersione) 'app_2' => 125, 'app_3' => 135, // Piano primo 'app_4' => 120, 'app_5' => 140, // Piano secondo 'app_6' => 130, 'app_7' => 100, // Mansarda (minor volume) 'app_8' => 105 // NO garage/cantine ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesaRiscaldamento($spesa_riscaldamento, $millesimi_riscaldamento); // Assert $this->assertEquals($spesa_riscaldamento, array_sum($distribuzione)); $this->assertArrayNotHasKey('garage_1', $distribuzione, 'Garage esclusi'); $this->assertArrayNotHasKey('cantina_1', $distribuzione, 'Cantine escluse'); // Verifica logica: piano terra paga di più $this->assertGreaterThan($distribuzione['app_7'], $distribuzione['app_1'], 'Piano terra > mansarda'); } ``` #### **Test Case 6: Ascensore (Escluso Piano Terra)** ```php public function test_ripartizione_ascensore_escluso_piano_terra() { // Setup $spesa_ascensore = 1800.00; $millesimi_ascensore = [ // Piano terra escluso 'app_3' => 200, // Piano primo 'app_4' => 180, 'app_5' => 220, // Piano secondo 'app_6' => 200, 'app_7' => 100, // Mansarda (peso ridotto) 'app_8' => 100 ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesaAscensore($spesa_ascensore, $millesimi_ascensore); // Assert $this->assertEquals($spesa_ascensore, array_sum($distribuzione)); $this->assertArrayNotHasKey('app_1', $distribuzione, 'Piano terra escluso'); $this->assertArrayNotHasKey('app_2', $distribuzione, 'Piano terra escluso'); // Verifica logica: piani alti pagano di più $this->assertGreaterThan($distribuzione['app_7'], $distribuzione['app_5'], 'Piano alto > mansarda'); } ``` ### 4️⃣ **Test Bilanci e Quadrature** #### **Test Case 7: Bilancio Completo** ```php public function test_bilancio_completo_quadratura() { // Setup - Dati anno completo $entrate = [ 'rate_condominiali' => 16800.00, 'interessi_mora' => 150.00, 'rimborsi' => 200.00 ]; $uscite = [ 'pulizie' => 3600.00, 'riscaldamento' => 4500.00, 'ascensore' => 2400.00, 'giardino' => 1200.00, 'amministrazione' => 1800.00, 'manutenzioni' => 1500.00, 'assicurazioni' => 800.00 ]; // Execute $bilancio = $this->contabilitaService->calcolaBilancio($entrate, $uscite); // Assert $totale_entrate = array_sum($entrate); $totale_uscite = array_sum($uscite); $saldo_atteso = $totale_entrate - $totale_uscite; $this->assertEquals($totale_entrate, $bilancio['totale_entrate']); $this->assertEquals($totale_uscite, $bilancio['totale_uscite']); $this->assertEquals($saldo_atteso, $bilancio['saldo']); $this->assertEquals($saldo_atteso, $bilancio['fondo_cassa'], 'Saldo = Fondo cassa'); } ``` #### **Test Case 8: Verifica Precision Decimal** ```php public function test_precision_database_decimal() { // Setup - Test precision database $importo_test = 12345.6789; // 4 decimali // Execute - Salvataggio in database $movimento = Movimento::create([ 'data' => now(), 'tipo' => 'entrata', 'importo' => $importo_test, 'descrizione' => 'Test precision' ]); // Assert - Verifica precision mantenuta $movimento_db = Movimento::find($movimento->id); $this->assertEquals(12345.68, $movimento_db->importo, 'Arrotondamento a 2 decimali'); $this->assertIsFloat($movimento_db->importo, 'Tipo float mantenuto'); } ``` --- ## 🎯 **TEST EDGE CASES** ### 🔍 **Edge Case 1: Millesimi Zero** ```php public function test_millesimi_zero_esclusi() { // Setup - Unità con millesimi 0 (es. parcheggi non computabili) $totale_spesa = 1000.00; $millesimi = [ 'unita_1' => 300, 'unita_2' => 400, 'unita_3' => 300, 'parcheggio_visitatori' => 0 // Non computabile ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert $this->assertEquals(0.00, $distribuzione['parcheggio_visitatori']); $this->assertEquals($totale_spesa, array_sum($distribuzione)); } ``` ### 🔍 **Edge Case 2: Importi Molto Piccoli** ```php public function test_importi_piccoli_precision() { // Setup - Spesa molto piccola con molte unità $totale_spesa = 0.50; // 50 centesimi $millesimi = array_fill(0, 100, 10); // 100 unità con 10 millesimi ciascuna // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert $this->assertEquals($totale_spesa, array_sum($distribuzione)); // Verifica che nessuna quota sia negativa foreach ($distribuzione as $quota) { $this->assertGreaterThanOrEqual(0.00, $quota); } } ``` ### 🔍 **Edge Case 3: Importi Molto Grandi** ```php public function test_importi_grandi_precision() { // Setup - Lavori straordinari importanti $totale_spesa = 250000.00; // 250k euro $millesimi = [ 'unita_1' => 150, 'unita_2' => 850 // Unità molto grande (85%) ]; // Execute $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); // Assert $this->assertEquals($totale_spesa, array_sum($distribuzione)); $this->assertEquals(37500.00, $distribuzione['unita_1']); // 15% $this->assertEquals(212500.00, $distribuzione['unita_2']); // 85% } ``` --- ## 🚀 **PERFORMANCE TESTS** ### ⚡ **Test Performance Calcoli** ```php public function test_performance_calcoli_grandi_dataset() { // Setup - Condominio molto grande $totale_spesa = 50000.00; $millesimi = []; // 1000 unità (stress test) for ($i = 1; $i <= 1000; $i++) { $millesimi["unita_$i"] = rand(5, 50); // Millesimi casuali } // Execute con timing $start = microtime(true); $distribuzione = $this->contabilitaService->distribuisciSpesa($totale_spesa, $millesimi); $tempo_esecuzione = microtime(true) - $start; // Assert $this->assertEquals($totale_spesa, array_sum($distribuzione)); $this->assertLessThan(1.0, $tempo_esecuzione, 'Calcolo < 1 secondo per 1000 unità'); $this->assertCount(1000, $distribuzione, 'Tutte le unità processate'); } ``` ### ⚡ **Test Memory Usage** ```php public function test_memory_usage_calcoli() { // Setup $memoria_iniziale = memory_get_usage(); // Execute - Multiple calcoli for ($i = 0; $i < 100; $i++) { $millesimi = array_fill(0, 50, rand(10, 100)); $distribuzione = $this->contabilitaService->distribuisciSpesa(10000.00, $millesimi); } $memoria_finale = memory_get_usage(); $memoria_usata = $memoria_finale - $memoria_iniziale; // Assert - Memory leak check $this->assertLessThan(5 * 1024 * 1024, $memoria_usata, 'Memory usage < 5MB'); } ``` --- ## 🔧 **MOCK E TESTING HELPERS** ### 🛠️ **ContabilitaService Mock** ```php // File: tests/Mocks/ContabilitaServiceMock.php class ContabilitaServiceMock extends ContabilitaService { public $log_chiamate = []; public function distribuisciSpesa($totale, $millesimi) { $this->log_chiamate[] = [ 'metodo' => 'distribuisciSpesa', 'parametri' => compact('totale', 'millesimi'), 'timestamp' => now() ]; return parent::distribuisciSpesa($totale, $millesimi); } } ``` ### 🎯 **Assertion Helpers** ```php // File: tests/TestCase.php abstract class TestCase extends BaseTestCase { protected function assertQuadraturaPerfetta($importi, $totale_atteso, $message = '') { $totale_calcolato = array_sum($importi); $this->assertEquals( $totale_atteso, $totale_calcolato, $message ?: 'Quadratura totale non corretta' ); } protected function assertProporzioneCorretta($importo, $totale, $millesimi, $totale_millesimi, $delta = 0.01) { $proporzione_calcolata = $importo / $totale; $proporzione_attesa = $millesimi / $totale_millesimi; $this->assertEqualsWithDelta($proporzione_attesa, $proporzione_calcolata, $delta); } } ``` --- ## 📊 **COVERAGE REQUIREMENTS** ### 🎯 **Target Coverage Contabilità** ``` ContabilitaService: 100% (Critico) MillesimiService: 100% (Critico) BilancioService: 95% (Critico) RipartizioneService: 95% (Critico) FatturazioneService: 90% (Alto) ``` ### 📈 **Coverage Monitoring** ```bash # Comando per coverage specifico contabilità php artisan test --coverage-filter="app/Services/Contabilita*" # Report HTML coverage php artisan test --coverage-html storage/coverage/contabilita --filter ContabilitaTest # Check coverage minimum php artisan test --coverage-filter="app/Services" --min=90 ``` --- ## 🚨 **CONTINUOUS INTEGRATION** ### 🔄 **Test Automatici Pre-Commit** ```yaml # .github/workflows/contabilita-tests.yml name: Contabilità Tests on: [push, pull_request] jobs: contabilita: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.1' extensions: bcmath # Per precision calcoli - name: Test Contabilità run: php artisan test --group=contabilita --stop-on-failure - name: Test Coverage run: php artisan test --coverage --min=95 --filter=Contabilita ``` ### 🎯 **Quality Gates** ```php // File: tests/Quality/ContabilitaQualityTest.php public function test_contabilita_quality_gates() { // Gate 1: Code coverage > 95% $this->assertCoverage('ContabilitaService', 95); // Gate 2: Cyclomatic complexity < 10 $this->assertComplexity('ContabilitaService', 10); // Gate 3: No deprecated methods $this->assertNoDeprecated('ContabilitaService'); // Gate 4: All public methods tested $this->assertAllMethodsTested('ContabilitaService'); } ``` --- ## 📅 **CRONOGRAMA IMPLEMENTAZIONE** ### 📋 **Settimana 1** *(Corrente)* - [x] Definizione test case critici - [ ] Implementazione ContabilitaService corretto - [ ] Test distribuzione millesimi base - [ ] Fix arrotondamenti ### 📋 **Settimana 2** - [ ] Test ripartizioni speciali (riscaldamento, ascensore) - [ ] Test bilanci e quadrature - [ ] Performance tests - [ ] Edge cases coverage ### 📋 **Settimana 3** - [ ] Integration con database - [ ] Test precision decimal - [ ] Memory leak testing - [ ] CI/CD setup ### 📋 **Settimana 4** - [ ] User acceptance testing - [ ] Documentation test cases - [ ] Quality gates validation - [ ] Production readiness --- ## 📞 **SUPPORTO E RISORSE** ### 📚 **Documentazione Tecnica** - [PHP BCMath](https://www.php.net/manual/en/book.bc.php) - Precision arithmetic - [Laravel Testing](https://laravel.com/docs/testing) - Framework testing - [PHPUnit Assertions](https://phpunit.readthedocs.io/en/latest/) - Test assertions ### 🔧 **Tools Contabilità** - **Excel/LibreOffice**: Validazione calcoli manuali - **Calculator**: Verifica calcoli complessi - **Postman**: Test API contabilità (quando disponibile) --- ## ⚠️ **NOTE CRITICHE** ### 🚨 **ZERO TOLERANCE** > **Non sono ammessi errori nei calcoli contabili** > - Ogni centesimo deve essere tracciabile > - Bilanci sempre quadrati al centesimo > - Test 100% coverage sui servizi contabili > - Validazione manuale su calcoli complessi ### 🔍 **VERIFICA MANUALE** > Prima di ogni deploy, validazione manuale: > - Calcolo rate su almeno 3 stabili diversi > - Ripartizione spesa complessa con Excel > - Bilancio completo con quadratura > - Test edge cases con dati reali --- *🔄 Aggiornare test ad ogni modifica ContabilitaService* *📊 Eseguire suite completa prima di ogni commit* *⚠️ PRIORITÀ MASSIMA: Zero bug contabili in produzione*