17 KiB
17 KiB
🧮 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]
// 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
// 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
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
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
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
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)
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)
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
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
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
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
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
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
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
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
// 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
// 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
# 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
# .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
// 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)
- 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 - Precision arithmetic
- Laravel Testing - Framework testing
- PHPUnit Assertions - 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