netgescon-master/docs/10-IMPLEMENTAZIONE-CONTABILITA-PARTITA-DOPPIA-GESTIONI.md
Michele Windows e68ee85a18 🚀 CHECKPOINT STABILE - Sistema Contabile Avanzato
📋 AGGIUNTE PRINCIPALI:
- Sistema contabile partita doppia con gestioni multiple
- Documentazione implementazione completa
- Models Laravel: GestioneContabile, MovimentoPartitaDoppia
- Controller ContabilitaAvanzataController
- Migration sistema contabile completo
- Scripts automazione e trasferimento
- Manuali utente e checklist implementazione

📊 FILES PRINCIPALI:
- docs/10-IMPLEMENTAZIONE-CONTABILITA-PARTITA-DOPPIA-GESTIONI.md
- SPECIFICHE-SISTEMA-CONTABILE-COMPLETO.md
- netgescon-laravel/database/migrations/2025_07_20_100000_create_complete_accounting_system.php
- netgescon-laravel/app/Models/GestioneContabile.php

 CHECKPOINT SICURO PER ROLLBACK
2025-07-26 15:11:19 +02:00

604 lines
23 KiB
Markdown
Raw 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.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 💰 IMPLEMENTAZIONE SISTEMA CONTABILE PARTITA DOPPIA CON GESTIONI MULTIPLE
## 📋 **OVERVIEW IMPLEMENTAZIONE**
Sistema contabile condominiale in partita doppia con gestioni multiple (ORDINARIA, RISCALDAMENTO, STRAORDINARIA) integrate con chiusura e riapertura contabile classica.
---
## 🎯 **PRINCIPI FONDAMENTALI**
### 📅 **Gestioni Multiple per Condominio**
```
🏢 CONDOMINIO
├── 📊 GESTIONE ORDINARIA (spese comuni)
├── 🔥 GESTIONE RISCALDAMENTO (spese termiche)
└── ⚡ GESTIONE STRAORDINARIA (lavori straordinari)
```
### 💎 **Partita Doppia con Gestioni**
```sql
-- Ogni movimento contabile è collegato a:
registrazione_contabile {
gestione_id, -- FK alla tabella gestioni
tipo_gestione, -- ENUM('ORDINARIA','RISCALDAMENTO','STRAORDINARIA')
dare_totale, -- Totale DARE
avere_totale -- Totale AVERE (deve essere = dare_totale)
}
```
### 🔄 **Chiusura e Riapertura Contabile**
1. **CHIUSURA GESTIONE**: Movimenti di chiusura a Stato Patrimoniale e Conto Economico
2. **RIAPERTURA GESTIONE**: Riporto saldi patrimoniali alla gestione successiva
---
## 🗃️ **SCHEMA DATABASE AGGIORNATO**
### 1⃣ **Tabella: `gestioni_contabili` (ESTESA)**
```sql
-- AGGIORNAMENTO tabella esistente
ALTER TABLE gestioni_contabili ADD COLUMN IF NOT EXISTS tipo_gestione ENUM('ORDINARIA','RISCALDAMENTO','STRAORDINARIA') NOT NULL DEFAULT 'ORDINARIA';
ALTER TABLE gestioni_contabili ADD COLUMN IF NOT EXISTS gestione_precedente_id BIGINT UNSIGNED NULL;
ALTER TABLE gestioni_contabili ADD COLUMN IF NOT EXISTS saldi_apertura JSON NULL;
ALTER TABLE gestioni_contabili ADD COLUMN IF NOT EXISTS saldi_chiusura JSON NULL;
-- Indici aggiuntivi
ALTER TABLE gestioni_contabili ADD INDEX idx_tipo_gestione (tipo_gestione);
ALTER TABLE gestioni_contabili ADD INDEX idx_condominio_tipo (condominio_id, tipo_gestione);
```
### 2⃣ **Tabella: `registrazioni_contabili` (AGGIORNATA)**
```sql
-- AGGIORNAMENTO per gestioni multiple
ALTER TABLE registrazioni_contabili ADD COLUMN IF NOT EXISTS tipo_gestione ENUM('ORDINARIA','RISCALDAMENTO','STRAORDINARIA') NOT NULL DEFAULT 'ORDINARIA';
ALTER TABLE registrazioni_contabili ADD COLUMN IF NOT EXISTS ritenuta_acconto DECIMAL(10,4) DEFAULT 0;
ALTER TABLE registrazioni_contabili ADD COLUMN IF NOT EXISTS percentuale_ritenuta DECIMAL(5,2) DEFAULT 0;
ALTER TABLE registrazioni_contabili ADD COLUMN IF NOT EXISTS codice_ritenuta VARCHAR(10);
ALTER TABLE registrazioni_contabili ADD COLUMN IF NOT EXISTS imponibile_ritenuta DECIMAL(12,4) DEFAULT 0;
-- Indici per performance
ALTER TABLE registrazioni_contabili ADD INDEX idx_gestione_tipo (gestione_id, tipo_gestione);
ALTER TABLE registrazioni_contabili ADD INDEX idx_ritenuta (ritenuta_acconto);
```
### 3⃣ **Nuova Tabella: `ripartizioni_gestioni`**
```sql
CREATE TABLE ripartizioni_gestioni (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
registrazione_id BIGINT UNSIGNED NOT NULL,
movimento_id BIGINT UNSIGNED NOT NULL,
-- 🎯 COLLEGAMENTO GESTIONI
gestione_ordinaria_perc DECIMAL(5,2) DEFAULT 0, -- % su gestione ordinaria
gestione_riscaldamento_perc DECIMAL(5,2) DEFAULT 0, -- % su gestione riscaldamento
gestione_straordinaria_perc DECIMAL(5,2) DEFAULT 0, -- % su gestione straordinaria
-- 💰 IMPORTI RIPARTITI
importo_ordinaria DECIMAL(12,4) DEFAULT 0,
importo_riscaldamento DECIMAL(12,4) DEFAULT 0,
importo_straordinaria DECIMAL(12,4) DEFAULT 0,
-- 📊 TABELLE MILLESIMALI
tabella_ordinaria VARCHAR(50) DEFAULT 'GENERALE',
tabella_riscaldamento VARCHAR(50) DEFAULT 'RISCALDAMENTO',
tabella_straordinaria VARCHAR(50) DEFAULT 'GENERALE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (registrazione_id) REFERENCES registrazioni_contabili(id) ON DELETE CASCADE,
FOREIGN KEY (movimento_id) REFERENCES movimenti_contabili(id) ON DELETE CASCADE,
INDEX idx_registrazione (registrazione_id),
INDEX idx_movimento (movimento_id),
-- CONSTRAINT: La somma delle percentuali deve essere 100%
CONSTRAINT chk_percentuali_totale CHECK (
gestione_ordinaria_perc + gestione_riscaldamento_perc + gestione_straordinaria_perc = 100
)
) ENGINE=InnoDB COMMENT='Ripartizione movimenti tra gestioni multiple';
```
### 4⃣ **Nuova Tabella: `chiusure_riaperture_contabili`**
```sql
CREATE TABLE chiusure_riaperture_contabili (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
gestione_id BIGINT UNSIGNED NOT NULL,
-- 🎯 TIPO OPERAZIONE
tipo_operazione ENUM('CHIUSURA','RIAPERTURA') NOT NULL,
data_operazione DATE NOT NULL,
-- 📊 DATI CHIUSURA/RIAPERTURA
saldi_conti JSON NOT NULL, -- Saldi di tutti i conti
totale_stato_patrimoniale DECIMAL(12,4), -- Totale SP
totale_conto_economico DECIMAL(12,4), -- Totale CE
risultato_gestione DECIMAL(12,4), -- Utile/Perdita
-- 🔗 COLLEGAMENTO RIAPERTURA
chiusura_precedente_id BIGINT UNSIGNED NULL, -- Link alla chiusura precedente
-- 📋 CONTROLLI
confermata BOOLEAN DEFAULT FALSE,
confermata_da BIGINT UNSIGNED,
confermata_il TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by BIGINT UNSIGNED NOT NULL,
FOREIGN KEY (gestione_id) REFERENCES gestioni_contabili(id) ON DELETE RESTRICT,
FOREIGN KEY (chiusura_precedente_id) REFERENCES chiusure_riaperture_contabili(id),
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE RESTRICT,
INDEX idx_gestione_tipo (gestione_id, tipo_operazione),
INDEX idx_data (data_operazione),
INDEX idx_confermata (confermata)
) ENGINE=InnoDB COMMENT='Chiusure e riaperture contabili per gestione';
```
---
## 🎨 **INTERFACCIA UTENTE - SPECIFICA SCHERMATA**
### 📝 **Maschera Registrazione Contabile Avanzata**
```php
// STRUTTURA FORM REGISTRAZIONE
[
// 📅 SEZIONE INTESTAZIONE
'data_operazione' => 'required|date',
'causale' => 'required|string|max:500',
'importo_totale' => 'required|numeric|min:0.01',
// 🎯 GESTIONI (CHECKBOX MULTIPLE)
'gestioni' => [
'ordinaria' => ['attiva' => true, 'percentuale' => 70],
'riscaldamento' => ['attiva' => true, 'percentuale' => 30],
'straordinaria' => ['attiva' => false, 'percentuale' => 0]
],
// 💰 SEZIONE RITENUTA D'ACCONTO
'ritenuta_section' => [
'applica_ritenuta' => true,
'codice_ritenuta' => 'R001', // Select da tabella ritenute
'percentuale_ritenuta' => 20.00,
'imponibile_ritenuta' => 1000.00,
'importo_ritenuta' => 200.00 // Calcolato automaticamente
],
// 📊 SEZIONE RIGHE CONTABILI (DARE/AVERE)
'righe_contabili' => [
[
'sottoconto_id' => 1,
'descrizione' => 'Pulizie scale marzo',
'importo_dare' => 1000.00,
'importo_avere' => 0,
'ripartizione_gestioni' => [
'ordinaria' => 70,
'riscaldamento' => 30
]
],
[
'sottoconto_id' => 2,
'descrizione' => 'Ritenuta d\'acconto 20%',
'importo_dare' => 200.00,
'importo_avere' => 0
],
[
'sottoconto_id' => 3,
'descrizione' => 'Pagamento bonifico',
'importo_dare' => 0,
'importo_avere' => 1200.00
]
]
]
```
### 🎨 **Layout Schermata HTML**
```html
<!-- FORM REGISTRAZIONE CONTABILE -->
<form id="registrazione-contabile-form">
<!-- SEZIONE INTESTAZIONE -->
<div class="card mb-4">
<div class="card-header">📅 Dati Generali Registrazione</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<label>Data Operazione</label>
<input type="date" name="data_operazione" class="form-control" required>
</div>
<div class="col-md-6">
<label>Causale</label>
<input type="text" name="causale" class="form-control" maxlength="500" required>
</div>
<div class="col-md-3">
<label>Importo Totale €</label>
<input type="number" name="importo_totale" class="form-control" step="0.01" required>
</div>
</div>
</div>
</div>
<!-- SEZIONE GESTIONI -->
<div class="card mb-4">
<div class="card-header">🎯 Ripartizione per Gestioni</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="gest_ordinaria" name="gestioni[ordinaria][attiva]" checked>
<label class="form-check-label">📊 Gestione Ordinaria</label>
</div>
<input type="number" name="gestioni[ordinaria][percentuale]" value="100" class="form-control mt-2" max="100" min="0">
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="gest_riscaldamento" name="gestioni[riscaldamento][attiva]">
<label class="form-check-label">🔥 Gestione Riscaldamento</label>
</div>
<input type="number" name="gestioni[riscaldamento][percentuale]" value="0" class="form-control mt-2" max="100" min="0">
</div>
<div class="col-md-4">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="gest_straordinaria" name="gestioni[straordinaria][attiva]">
<label class="form-check-label">⚡ Gestione Straordinaria</label>
</div>
<input type="number" name="gestioni[straordinaria][percentuale]" value="0" class="form-control mt-2" max="100" min="0">
</div>
</div>
<div class="alert alert-info mt-3">
<small>💡 Il totale delle percentuali deve essere 100%</small>
</div>
</div>
</div>
<!-- SEZIONE RITENUTA D'ACCONTO -->
<div class="card mb-4">
<div class="card-header">💰 Ritenuta d'Acconto</div>
<div class="card-body">
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="applica_ritenuta" name="applica_ritenuta">
<label class="form-check-label">Applica Ritenuta d'Acconto</label>
</div>
<div id="sezione-ritenuta" style="display: none;">
<div class="row">
<div class="col-md-3">
<label>Codice Ritenuta</label>
<select name="codice_ritenuta" class="form-control">
<option value="">Seleziona...</option>
<option value="R001">R001 - Ritenuta 20%</option>
<option value="R002">R002 - Ritenuta 4%</option>
</select>
</div>
<div class="col-md-3">
<label>% Ritenuta</label>
<input type="number" name="percentuale_ritenuta" class="form-control" step="0.01" max="100">
</div>
<div class="col-md-3">
<label>Imponibile €</label>
<input type="number" name="imponibile_ritenuta" class="form-control" step="0.01">
</div>
<div class="col-md-3">
<label>Importo Ritenuta €</label>
<input type="number" name="importo_ritenuta" class="form-control" step="0.01" readonly>
</div>
</div>
</div>
</div>
</div>
<!-- SEZIONE RIGHE CONTABILI -->
<div class="card mb-4">
<div class="card-header">📊 Righe Contabili (Partita Doppia)</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="righe-contabili-table">
<thead>
<tr>
<th width="25%">Sottoconto</th>
<th width="30%">Descrizione</th>
<th width="15%">DARE €</th>
<th width="15%">AVERE €</th>
<th width="10%">Gestioni</th>
<th width="5%">Azioni</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<select name="righe[0][sottoconto_id]" class="form-control" required>
<option value="">Seleziona sottoconto...</option>
<!-- Popolato via JavaScript -->
</select>
</td>
<td>
<input type="text" name="righe[0][descrizione]" class="form-control" maxlength="500">
</td>
<td>
<input type="number" name="righe[0][importo_dare]" class="form-control importo-dare" step="0.01" min="0">
</td>
<td>
<input type="number" name="righe[0][importo_avere]" class="form-control importo-avere" step="0.01" min="0">
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="configuraGestioni(0)">
⚙️ Config
</button>
</td>
<td>
<button type="button" class="btn btn-sm btn-danger" onclick="rimuoviRiga(0)">
🗑️
</button>
</td>
</tr>
</tbody>
<tfoot>
<tr class="table-info">
<th colspan="2">TOTALI</th>
<th id="totale-dare">0.00</th>
<th id="totale-avere">0.00</th>
<th colspan="2">
<span id="quadratura-status" class="badge badge-warning">⚠️ Non bilanciato</span>
</th>
</tr>
</tfoot>
</table>
<button type="button" class="btn btn-success mt-2" onclick="aggiungiRiga()">
Aggiungi Riga
</button>
</div>
</div>
</div>
<!-- PULSANTI -->
<div class="text-center">
<button type="button" class="btn btn-secondary" onclick="salvaBozza()">💾 Salva Bozza</button>
<button type="submit" class="btn btn-primary">✅ Conferma Registrazione</button>
<button type="button" class="btn btn-warning" onclick="calcolaRipartizione()">🧮 Calcola Ripartizione</button>
</div>
</form>
```
---
## ⚙️ **WORKFLOW IMPLEMENTAZIONE**
### 1⃣ **Models Laravel**
```php
// GestioneContabile.php (AGGIORNATO)
class GestioneContabile extends Model {
protected $fillable = [
'condominio_id', 'denominazione', 'tipo_gestione',
'data_inizio', 'data_fine_prevista', 'stato',
'gestione_precedente_id', 'saldi_apertura', 'saldi_chiusura'
];
protected $casts = [
'saldi_apertura' => 'array',
'saldi_chiusura' => 'array',
'data_inizio' => 'date',
'data_fine_prevista' => 'date'
];
// Relazione con registrazioni
public function registrazioni() {
return $this->hasMany(RegistrazioneContabile::class, 'gestione_id');
}
// Calcolo saldi automatico
public function calcolaSaldi() {
// Implementazione calcolo saldi DARE/AVERE
}
}
// RegistrazioneContabile.php (AGGIORNATO)
class RegistrazioneContabile extends Model {
protected $fillable = [
'gestione_id', 'tipo_gestione', 'data_operazione',
'causale', 'importo_totale', 'ritenuta_acconto',
'percentuale_ritenuta', 'codice_ritenuta'
];
// Relazione gestioni multiple
public function ripartizioniGestioni() {
return $this->hasMany(RipartizioneGestione::class, 'registrazione_id');
}
// Verifica quadratura
public function isQuadrata() {
$dare = $this->movimenti()->sum('importo_dare');
$avere = $this->movimenti()->sum('importo_avere');
return abs($dare - $avere) < 0.01;
}
}
```
### 2⃣ **Controller Avanzato**
```php
// ContabilitaAvanzataController.php
class ContabilitaAvanzataController extends Controller {
public function creaRegistrazione(Request $request) {
DB::beginTransaction();
try {
// 1. Validazione input
$validated = $this->validateRegistrazione($request);
// 2. Creazione registrazione principale
$registrazione = $this->creaRegistrazionePrincipale($validated);
// 3. Creazione movimenti DARE/AVERE
$this->creaMovimentiContabili($registrazione, $validated['righe']);
// 4. Gestione ripartizioni multiple gestioni
$this->creaRipartizioniGestioni($registrazione, $validated['gestioni']);
// 5. Calcolo ripartizione condomini
$this->calcolaRipartizioneCondomini($registrazione);
// 6. Verifica quadratura finale
if (!$registrazione->isQuadrata()) {
throw new Exception('Partita doppia non bilanciata');
}
DB::commit();
return response()->json(['success' => true, 'id' => $registrazione->id]);
} catch (Exception $e) {
DB::rollback();
return response()->json(['error' => $e->getMessage()], 400);
}
}
public function chiusuraGestione($gestioneId) {
// Implementazione chiusura contabile
$gestione = GestioneContabile::findOrFail($gestioneId);
// 1. Calcolo saldi finali tutti i sottoconti
$saldiFinali = $this->calcolaSaldiFinali($gestione);
// 2. Creazione registrazioni di chiusura
$this->creaRegistrazioniChiusura($gestione, $saldiFinali);
// 3. Aggiornamento stato gestione
$gestione->update(['stato' => 'chiusa_definitiva']);
}
public function riaperturaGestione($nuovaGestioneId, $precedenteGestioneId) {
// Implementazione riapertura con riporto saldi
}
}
```
### 3⃣ **JavaScript Frontend**
```javascript
// contabilita-avanzata.js
class ContabilitaAvanzata {
constructor() {
this.inizializzaForm();
this.bindEvents();
}
inizializzaForm() {
// Inizializzazione form e validazioni
this.calcolaTotali();
this.verificaQuadratura();
}
aggiungiRiga() {
// Aggiunge una nuova riga alla tabella movimenti
const nuovaRiga = this.creaNuovaRigaTemplate();
$('#righe-contabili-table tbody').append(nuovaRiga);
this.bindEventsRiga();
}
calcolaTotali() {
let totaleDare = 0;
let totaleAvere = 0;
$('.importo-dare').each(function() {
totaleDare += parseFloat($(this).val()) || 0;
});
$('.importo-avere').each(function() {
totaleAvere += parseFloat($(this).val()) || 0;
});
$('#totale-dare').text(totaleDare.toFixed(2));
$('#totale-avere').text(totaleAvere.toFixed(2));
this.verificaQuadratura(totaleDare, totaleAvere);
}
verificaQuadratura(dare, avere) {
const differenza = Math.abs(dare - avere);
const status = $('#quadratura-status');
if (differenza < 0.01) {
status.removeClass('badge-warning badge-danger')
.addClass('badge-success')
.text('✅ Bilanciato');
} else {
status.removeClass('badge-success badge-danger')
.addClass('badge-warning')
.text(`⚠️ Differenza: ${differenza.toFixed(2)}`);
}
}
calcolaRipartizione() {
// Calcola la ripartizione automatica tra gestioni
const formData = new FormData($('#registrazione-contabile-form')[0]);
$.post('/api/calcola-ripartizione', formData)
.done(function(response) {
// Aggiorna la UI con i risultati
this.aggiornaRipartizione(response.ripartizioni);
}.bind(this));
}
salvaRegistrazione() {
if (!this.validaForm()) return;
const formData = new FormData($('#registrazione-contabile-form')[0]);
$.post('/api/registrazioni-contabili', formData)
.done(function(response) {
Swal.fire('Successo!', 'Registrazione salvata correttamente', 'success');
window.location.href = '/admin/contabilita';
})
.fail(function(xhr) {
Swal.fire('Errore!', xhr.responseJSON.error, 'error');
});
}
}
// Inizializzazione
$(document).ready(function() {
new ContabilitaAvanzata();
});
```
---
## 📋 **CHECKLIST IMPLEMENTAZIONE**
### ✅ **Database**
- [ ] Eseguire migration gestioni multiple
- [ ] Aggiornare tabelle esistenti
- [ ] Creare nuove tabelle ripartizioni
- [ ] Implementare trigger calcolo saldi
### ✅ **Backend Laravel**
- [ ] Aggiornare Models esistenti
- [ ] Creare ContabilitaAvanzataController
- [ ] Implementare API per calcoli
- [ ] Creare Service per chiusura/riapertura
### ✅ **Frontend**
- [ ] Creare form registrazione avanzato
- [ ] Implementare JavaScript calcoli
- [ ] Integrare con sistema esistente
- [ ] Test interfaccia utente
### ✅ **Integrazione**
- [ ] Collegare con sistema esistente stabili
- [ ] Implementare ripartizione millesimale
- [ ] Test con dati reali
- [ ] Validazione chiusura/riapertura
---
## 🎯 **PROSSIMI PASSI**
1. **📤 TRASFERIMENTO DOCUMENTAZIONE**: Copia questo file sulla VM
2. **💾 IMPLEMENTAZIONE DATABASE**: Eseguire le migration
3. **🎨 SVILUPPO INTERFACCIA**: Creare le schermate
4. **🔄 INTEGRAZIONE**: Collegare con sistema esistente
5. **📊 TEST**: Validare con dati reali
Questa documentazione fornisce tutte le specifiche per implementare il sistema contabile avanzato con gestioni multiple e partita doppia classica! 🚀💰