netgescon-master/docs/logs/logs-laravel/TEST_CONTABILITA.md
Pikappa2 480e7eafbd 🎯 NETGESCON - Setup iniziale repository completo
📋 Commit iniziale con:
-  Documentazione unificata in docs/
-  Codice Laravel in netgescon-laravel/
-  Script automazione in scripts/
-  Configurazione sync rsync
-  Struttura organizzata e pulita

🔄 Versione: 2025.07.19-1644
🎯 Sistema pronto per Git distribuito
2025-07-19 16:44:47 +02:00

590 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🧮 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*