netgescon-master/docs/02-architettura-laravel/09-sistema-contabile/SISTEMA-CONTABILE-PARTITA-DOPPIA.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

829 lines
31 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.

# 🏦 SISTEMA CONTABILE NETGESCON - PARTITA DOPPIA AVANZATA
## 📋 OVERVIEW
Sistema contabile completo per condomini basato su partita doppia con gestione multi-esercizio, multi-gestione e protocolli separati.
## 🆕 NUOVE SPECIFICHE IMPLEMENTATE
- **Maschera Unica di Registrazione:** Form unificato per tutte le tipologie di documenti
- **Tabelle Millesimali Strutturate:** Gestione completa dei parametri di ripartizione
- **Riconciliazione Bancaria Avanzata:** Algoritmi automatici per matching movimenti
- **Triggers Automatici:** Aggiornamento saldi in real-time
- **Backup Granulari:** Backup per singolo condominio con restore selettivo
- **Compliance Fiscale:** Gestione automatica adempimenti fiscali e ritenute
## 🎯 PRINCIPI FONDAMENTALI
### 🔄 Partita Doppia Condominiale
```
DARE / AVERE = SEMPRE PAREGGIATO
COSTI / RICAVI
CREDITI / DEBITI
ENTRATE / USCITE
ATTIVITÀ / PASSIVITÀ
```
### 🏢 "CEO Model" - Amministratore come CEO
- **Zero Utili:** Condominio non può avere utili, solo pareggio
- **Zero Sotto-Cassa:** Mai scendere sotto 0 nelle risorse
- **Bilancio Sempre Quadrato:** Attività = Passività + Patrimonio
- **Audit Completo:** Tracking di ogni modifica con chi/quando
### 📊 Gestione Multi-Protocollo
- **Protocollo Generale:** Numerazione progressiva annuale (2025/0001, 2025/0002...)
- **Protocolli per Gestione:** ORD2025/001, RISC2025/001, STR2025/001
- **Protocolli Bancari:** Separati per ogni conto corrente
- **Protocolli Fiscali:** Per ritenute, F24, dichiarazioni
---
## 🗂️ STRUTTURA DATABASE CONTABILE
### 1⃣ Piano dei Conti (3 Livelli)
```sql
CREATE TABLE piano_conti (
id INT PRIMARY KEY AUTO_INCREMENT,
condominio_id INT NOT NULL,
codice VARCHAR(10) NOT NULL, -- 01.001.0001 (MASTRO.CONTO.SOTTOCONTO)
mastro VARCHAR(2) NOT NULL, -- 01
conto VARCHAR(3) NOT NULL, -- 001
sottoconto VARCHAR(4) NOT NULL, -- 0001
denominazione VARCHAR(255) NOT NULL,
tipo_conto ENUM('ATTIVO','PASSIVO','COSTO','RICAVO','PATRIMONIALE') NOT NULL,
natura ENUM('DARE','AVERE') NOT NULL,
gestione ENUM('TUTTE','ORDINARIA','RISCALDAMENTO','STRAORDINARIA') DEFAULT 'TUTTE',
centro_costo VARCHAR(50) NULL, -- Per ripartizioni specifiche
attivo BOOLEAN DEFAULT TRUE,
saldo_dare DECIMAL(12,2) DEFAULT 0.00,
saldo_avere DECIMAL(12,2) DEFAULT 0.00,
saldo_finale DECIMAL(12,2) GENERATED ALWAYS AS (saldo_dare - saldo_avere) STORED,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by INT,
updated_by INT,
UNIQUE KEY unique_conto_condominio (condominio_id, codice),
FOREIGN KEY (condominio_id) REFERENCES condomini(id),
INDEX idx_tipo_natura (tipo_conto, natura),
INDEX idx_gestione (gestione),
INDEX idx_centro_costo (centro_costo)
);
```
### 🆕 2⃣ Tabelle Millesimali Strutturate
```sql
CREATE TABLE tabelle_millesimali (
id INT PRIMARY KEY AUTO_INCREMENT,
condominio_id INT NOT NULL,
denominazione VARCHAR(255) NOT NULL, -- "Millesimi Generali", "Riscaldamento", "Ascensore Piano 1-3"
tipo_tabella ENUM('GENERALE','RISCALDAMENTO','ASCENSORE','SCALE','CORTILE','SPECIFICO') NOT NULL,
descrizione TEXT,
data_approvazione DATE NULL,
verbale_assemblea VARCHAR(255) NULL,
attiva BOOLEAN DEFAULT TRUE,
formula_calcolo TEXT NULL, -- Formula matematica per calcolo automatico
parametri_calcolo JSON NULL, -- {"metri_quadri": true, "vani": true, "piano": {"peso": 0.8}}
totale_millesimi DECIMAL(8,3) DEFAULT 1000.000,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (condominio_id) REFERENCES condomini(id),
INDEX idx_tipo_attiva (tipo_tabella, attiva)
);
CREATE TABLE righe_millesimali (
id INT PRIMARY KEY AUTO_INCREMENT,
tabella_id INT NOT NULL,
unita_immobiliare_id INT NOT NULL,
millesimi DECIMAL(8,3) NOT NULL,
percentuale DECIMAL(6,3) GENERATED ALWAYS AS (millesimi / 1000 * 100) STORED,
metri_quadri DECIMAL(8,2) NULL,
vani DECIMAL(4,1) NULL,
piano INT NULL,
categoria_catastale VARCHAR(10) NULL,
note TEXT,
FOREIGN KEY (tabella_id) REFERENCES tabelle_millesimali(id) ON DELETE CASCADE,
FOREIGN KEY (unita_immobiliare_id) REFERENCES unita_immobiliari(id),
UNIQUE KEY unique_tabella_unita (tabella_id, unita_immobiliare_id),
INDEX idx_millesimi (millesimi),
INDEX idx_piano_categoria (piano, categoria_catastale)
);
```
### 🆕 3⃣ Sistema Ripartizioni Automatiche
```sql
CREATE TABLE regole_ripartizione (
id INT PRIMARY KEY AUTO_INCREMENT,
condominio_id INT NOT NULL,
codice_regola VARCHAR(50) NOT NULL, -- "SPESE_GENERALI", "RISCALDAMENTO_CENTRALE", etc.
denominazione VARCHAR(255) NOT NULL,
conto_id INT NOT NULL, -- Conto del piano dei conti a cui applicare
tabella_millesimale_id INT NOT NULL,
tipo_ripartizione ENUM('MILLESIMI','TESTE','UNITA','METRI_QUADRI','CUSTOM') NOT NULL,
formula_custom TEXT NULL, -- Formula personalizzata se tipo=CUSTOM
soglia_minima DECIMAL(10,2) DEFAULT 0.00, -- Sotto questa soglia non ripartire
attiva BOOLEAN DEFAULT TRUE,
FOREIGN KEY (condominio_id) REFERENCES condomini(id),
FOREIGN KEY (conto_id) REFERENCES piano_conti(id),
FOREIGN KEY (tabella_millesimale_id) REFERENCES tabelle_millesimali(id),
UNIQUE KEY unique_conto_regola (condominio_id, conto_id),
INDEX idx_attiva (attiva)
);
```
### 🆕 4⃣ Maschera Unica di Registrazione
```sql
CREATE TABLE documenti_contabili (
id INT PRIMARY KEY AUTO_INCREMENT,
condominio_id INT NOT NULL,
gestione_id INT NOT NULL,
numero_protocollo VARCHAR(20) NOT NULL, -- Protocollo unificato
data_documento DATE NOT NULL,
data_registrazione DATE NOT NULL,
data_competenza_da DATE NOT NULL,
data_competenza_a DATE NOT NULL,
data_scadenza DATE NULL,
-- TIPO DOCUMENTO
tipo_documento ENUM('FATTURA_ATTIVA','FATTURA_PASSIVA','RICEVUTA','BONIFICO',
'VERSAMENTO','NOTA_CREDITO','NOTA_DEBITO','GIROCONTO',
'REGISTRAZIONE_MANUALE','STORNO') NOT NULL,
categoria_documento VARCHAR(100) NULL, -- "Manutenzione", "Utenze", "Assicurazioni"
-- SOGGETTI
fornitore_id INT NULL,
condomino_id INT NULL,
descrizione_soggetto VARCHAR(255) NULL, -- Per soggetti occasionali
-- IMPORTI E FISCALE
importo_imponibile DECIMAL(10,2) DEFAULT 0.00,
importo_iva DECIMAL(10,2) DEFAULT 0.00,
importo_totale DECIMAL(10,2) NOT NULL,
ritenuta_acconto DECIMAL(10,2) DEFAULT 0.00,
percentuale_ritenuta DECIMAL(5,2) DEFAULT 0.00,
causale_ritenuta VARCHAR(100) NULL,
codice_iva VARCHAR(20) NULL,
split_payment BOOLEAN DEFAULT FALSE,
reverse_charge BOOLEAN DEFAULT FALSE,
-- DOCUMENTO ORIGINALE
numero_documento VARCHAR(100) NULL,
serie_documento VARCHAR(20) NULL,
data_documento_originale DATE NULL,
-- WORKFLOW E STATO
stato ENUM('BOZZA','VALIDATO','CONTABILIZZATO','PAGATO','INCASSATO','ANNULLATO') DEFAULT 'BOZZA',
workflow_step ENUM('INSERIMENTO','VALIDAZIONE','CONTABILIZZAZIONE','PAGAMENTO','CHIUSURA') DEFAULT 'INSERIMENTO',
-- RIPARTIZIONE AUTOMATICA
ripartizione_automatica BOOLEAN DEFAULT TRUE,
regola_ripartizione_id INT NULL,
tabella_millesimale_id INT NULL,
-- TRACKING E AUDIT
note TEXT,
urgente BOOLEAN DEFAULT FALSE,
validato_da INT NULL,
validato_at TIMESTAMP NULL,
contabilizzato_da INT NULL,
contabilizzato_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_by INT NOT NULL,
updated_by INT,
FOREIGN KEY (condominio_id) REFERENCES condomini(id),
FOREIGN KEY (gestione_id) REFERENCES gestioni_contabili(id),
FOREIGN KEY (fornitore_id) REFERENCES fornitori(id),
FOREIGN KEY (condomino_id) REFERENCES condomini_proprietari(id),
FOREIGN KEY (regola_ripartizione_id) REFERENCES regole_ripartizione(id),
FOREIGN KEY (tabella_millesimale_id) REFERENCES tabelle_millesimali(id),
INDEX idx_protocollo (numero_protocollo),
INDEX idx_stato_workflow (stato, workflow_step),
INDEX idx_competenza (data_competenza_da, data_competenza_a),
INDEX idx_scadenze (data_scadenza, stato)
);
```
### 🆕 5⃣ Riconciliazione Bancaria Avanzata
```sql
CREATE TABLE riconciliazioni_bancarie (
id INT PRIMARY KEY AUTO_INCREMENT,
conto_bancario_id INT NOT NULL,
periodo_da DATE NOT NULL,
periodo_a DATE NOT NULL,
saldo_estratto_conto DECIMAL(12,2) NOT NULL,
saldo_contabile DECIMAL(12,2) NOT NULL,
differenza DECIMAL(12,2) GENERATED ALWAYS AS (saldo_estratto_conto - saldo_contabile) STORED,
stato ENUM('APERTA','RICONCILIATA','CHIUSA','SOSPESA') DEFAULT 'APERTA',
algoritmo_matching ENUM('AUTOMATICO','MANUALE','MISTO') DEFAULT 'AUTOMATICO',
soglia_matching DECIMAL(10,2) DEFAULT 0.01, -- Tolleranza per matching automatico
movimenti_non_riconciliati INT DEFAULT 0,
note TEXT,
riconciliata_da INT NULL,
riconciliata_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (conto_bancario_id) REFERENCES conti_bancari(id),
FOREIGN KEY (riconciliata_da) REFERENCES users(id),
INDEX idx_periodo (periodo_da, periodo_a),
INDEX idx_stato (stato)
);
CREATE TABLE movimenti_riconciliazione (
id INT PRIMARY KEY AUTO_INCREMENT,
riconciliazione_id INT NOT NULL,
movimento_bancario_id INT NULL, -- Movimento da estratto conto
transazione_contabile_id INT NULL, -- Movimento da contabilità
tipo_matching ENUM('ESATTO','APPROSSIMATO','MANUALE','AUTOMATICO') NOT NULL,
confidence_score DECIMAL(3,2) DEFAULT 1.00, -- Punteggio di affidabilità match
differenza_importo DECIMAL(10,2) DEFAULT 0.00,
differenza_data INT DEFAULT 0, -- Giorni di differenza
note_riconciliazione TEXT,
validato BOOLEAN DEFAULT FALSE,
FOREIGN KEY (riconciliazione_id) REFERENCES riconciliazioni_bancarie(id) ON DELETE CASCADE,
FOREIGN KEY (movimento_bancario_id) REFERENCES movimenti_bancari(id),
FOREIGN KEY (transazione_contabile_id) REFERENCES transazioni_contabili(id),
INDEX idx_matching (tipo_matching, confidence_score),
INDEX idx_validato (validato)
);
```
### 🆕 6⃣ Sistema Backup Granulari
```sql
CREATE TABLE backup_snapshot (
id INT PRIMARY KEY AUTO_INCREMENT,
condominio_id INT NULL, -- NULL = backup completo sistema
tipo_backup ENUM('COMPLETO','INCREMENTALE','CONDOMINIO','GESTIONE','TABELLA') NOT NULL,
tabella_target VARCHAR(100) NULL, -- Per backup di singola tabella
gestione_id INT NULL, -- Per backup di singola gestione
descrizione VARCHAR(255) NOT NULL,
dimensione_bytes BIGINT DEFAULT 0,
percorso_file VARCHAR(500) NOT NULL,
hash_integrità VARCHAR(64) NOT NULL, -- SHA-256 del file
compresso BOOLEAN DEFAULT TRUE,
crittografato BOOLEAN DEFAULT TRUE,
password_hash VARCHAR(255) NULL,
stato ENUM('IN_CORSO','COMPLETATO','FALLITO','CORROTTO') DEFAULT 'IN_CORSO',
data_inizio TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
data_fine TIMESTAMP NULL,
durata_secondi INT NULL,
created_by INT NOT NULL,
FOREIGN KEY (condominio_id) REFERENCES condomini(id),
FOREIGN KEY (gestione_id) REFERENCES gestioni_contabili(id),
FOREIGN KEY (created_by) REFERENCES users(id),
INDEX idx_tipo_stato (tipo_backup, stato),
INDEX idx_condominio_data (condominio_id, data_inizio)
);
CREATE TABLE restore_log (
id INT PRIMARY KEY AUTO_INCREMENT,
backup_snapshot_id INT NOT NULL,
tipo_restore ENUM('COMPLETO','PARZIALE','TABELLA','RECORD') NOT NULL,
target_condominio_id INT NULL,
target_gestione_id INT NULL,
filtri_restore JSON NULL, -- Filtri applicati durante restore
records_processati INT DEFAULT 0,
records_restaurati INT DEFAULT 0,
records_saltati INT DEFAULT 0,
errori_riscontrati INT DEFAULT 0,
log_dettaglio LONGTEXT NULL,
stato ENUM('IN_CORSO','COMPLETATO','FALLITO','ANNULLATO') DEFAULT 'IN_CORSO',
data_inizio TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
data_fine TIMESTAMP NULL,
restaurato_da INT NOT NULL,
FOREIGN KEY (backup_snapshot_id) REFERENCES backup_snapshot(id),
FOREIGN KEY (target_condominio_id) REFERENCES condomini(id),
FOREIGN KEY (target_gestione_id) REFERENCES gestioni_contabili(id),
FOREIGN KEY (restaurato_da) REFERENCES users(id),
INDEX idx_stato_data (stato, data_inizio)
);
```
---
## 🆕 TRIGGERS AUTOMATICI AVANZATI
### 📊 Trigger Aggiornamento Saldi Real-Time
```sql
DELIMITER //
-- Trigger per aggiornamento saldi piano conti
CREATE TRIGGER tr_aggiorna_saldi_piano_conti_insert
AFTER INSERT ON righe_movimenti_contabili
FOR EACH ROW
BEGIN
IF NEW.tipo_movimento = 'DARE' THEN
UPDATE piano_conti
SET saldo_dare = saldo_dare + NEW.importo,
updated_at = CURRENT_TIMESTAMP,
updated_by = NEW.created_by
WHERE id = NEW.conto_id;
ELSE
UPDATE piano_conti
SET saldo_avere = saldo_avere + NEW.importo,
updated_at = CURRENT_TIMESTAMP,
updated_by = NEW.created_by
WHERE id = NEW.conto_id;
END IF;
END//
-- Trigger per aggiornamento saldi bancari
CREATE TRIGGER tr_aggiorna_saldi_bancari
AFTER INSERT ON movimenti_bancari
FOR EACH ROW
BEGIN
DECLARE delta_importo DECIMAL(12,2);
IF NEW.tipo_movimento = 'ENTRATA' THEN
SET delta_importo = NEW.importo;
ELSE
SET delta_importo = -NEW.importo;
END IF;
UPDATE conti_bancari
SET saldo_attuale = saldo_attuale + delta_importo,
data_ultimo_movimento = NEW.data_movimento,
updated_at = CURRENT_TIMESTAMP
WHERE id = NEW.conto_bancario_id;
END//
-- Trigger controllo quadratura automatica
CREATE TRIGGER tr_verifica_quadratura
AFTER INSERT ON righe_movimenti_contabili
FOR EACH ROW
BEGIN
DECLARE totale_dare DECIMAL(12,2) DEFAULT 0;
DECLARE totale_avere DECIMAL(12,2) DEFAULT 0;
SELECT
COALESCE(SUM(CASE WHEN tipo_movimento = 'DARE' THEN importo ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN tipo_movimento = 'AVERE' THEN importo ELSE 0 END), 0)
INTO totale_dare, totale_avere
FROM righe_movimenti_contabili
WHERE transazione_id = NEW.transazione_id;
UPDATE transazioni_contabili
SET quadratura_ok = (totale_dare = totale_avere),
importo_totale = totale_dare,
updated_at = CURRENT_TIMESTAMP
WHERE id = NEW.transazione_id;
END//
-- Trigger per ripartizione automatica
CREATE TRIGGER tr_ripartizione_automatica
AFTER UPDATE ON documenti_contabili
FOR EACH ROW
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_unita_id INT;
DECLARE v_millesimi DECIMAL(8,3);
DECLARE v_importo_ripartito DECIMAL(10,2);
DECLARE cur_unita CURSOR FOR
SELECT ui.id, rm.millesimi
FROM unita_immobiliari ui
JOIN righe_millesimali rm ON ui.id = rm.unita_immobiliare_id
WHERE rm.tabella_id = NEW.tabella_millesimale_id
AND ui.condominio_id = NEW.condominio_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- Esegui ripartizione solo se documento validato e ripartizione automatica abilitata
IF NEW.stato = 'VALIDATO' AND NEW.ripartizione_automatica = TRUE
AND OLD.stato != 'VALIDATO' AND NEW.tabella_millesimale_id IS NOT NULL THEN
OPEN cur_unita;
read_loop: LOOP
FETCH cur_unita INTO v_unita_id, v_millesimi;
IF done THEN
LEAVE read_loop;
END IF;
SET v_importo_ripartito = (NEW.importo_totale * v_millesimi / 1000);
-- Inserisci riga di ripartizione per ogni unità immobiliare
INSERT INTO ripartizioni_spese (
documento_id, unita_immobiliare_id, importo_ripartito,
millesimi_applicati, created_at
) VALUES (
NEW.id, v_unita_id, v_importo_ripartito,
v_millesimi, CURRENT_TIMESTAMP
);
END LOOP;
CLOSE cur_unita;
END IF;
END//
DELIMITER ;
```
### 🆕 Stored Procedures per Operazioni Complesse
```sql
DELIMITER //
-- Procedura per chiusura gestione contabile
CREATE PROCEDURE sp_chiudi_gestione_contabile(
IN p_gestione_id INT,
IN p_user_id INT,
OUT p_result VARCHAR(255)
)
BEGIN
DECLARE v_totale_costi DECIMAL(12,2) DEFAULT 0;
DECLARE v_totale_ricavi DECIMAL(12,2) DEFAULT 0;
DECLARE v_conguaglio DECIMAL(12,2) DEFAULT 0;
DECLARE v_stato_attuale VARCHAR(20);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET p_result = 'ERRORE: Impossibile chiudere la gestione';
END;
START TRANSACTION;
-- Verifica stato gestione
SELECT stato INTO v_stato_attuale
FROM gestioni_contabili
WHERE id = p_gestione_id;
IF v_stato_attuale != 'APERTA' THEN
SET p_result = 'ERRORE: La gestione non è in stato APERTA';
ROLLBACK;
ELSE
-- Calcola totali
SELECT
COALESCE(SUM(CASE WHEN pc.tipo_conto = 'COSTO' THEN pc.saldo_finale ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN pc.tipo_conto = 'RICAVO' THEN pc.saldo_finale ELSE 0 END), 0)
INTO v_totale_costi, v_totale_ricavi
FROM piano_conti pc
JOIN gestioni_contabili gc ON pc.condominio_id = gc.condominio_id
WHERE gc.id = p_gestione_id
AND (pc.gestione = gc.tipo_gestione OR pc.gestione = 'TUTTE');
SET v_conguaglio = v_totale_ricavi - v_totale_costi;
-- Aggiorna gestione
UPDATE gestioni_contabili
SET stato = 'CHIUSA',
data_chiusura = CURRENT_DATE,
totale_costi = v_totale_costi,
totale_ricavi = v_totale_ricavi,
conguaglio = v_conguaglio,
saldo_finale = v_conguaglio,
updated_at = CURRENT_TIMESTAMP,
updated_by = p_user_id
WHERE id = p_gestione_id;
-- Crea bilancio di chiusura
INSERT INTO bilanci_chiusura (
condominio_id, gestione_id, data_chiusura,
totale_costi, totale_ricavi, conguaglio_gestione
)
SELECT condominio_id, id, CURRENT_DATE,
v_totale_costi, v_totale_ricavi, v_conguaglio
FROM gestioni_contabili
WHERE id = p_gestione_id;
COMMIT;
SET p_result = CONCAT('SUCCESS: Gestione chiusa. Conguaglio: €', v_conguaglio);
END IF;
END//
-- Procedura per riconciliazione bancaria automatica
CREATE PROCEDURE sp_riconciliazione_automatica(
IN p_conto_bancario_id INT,
IN p_periodo_da DATE,
IN p_periodo_a DATE,
IN p_soglia_matching DECIMAL(10,2),
OUT p_movimenti_riconciliati INT
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_mov_bancario_id INT;
DECLARE v_mov_importo DECIMAL(10,2);
DECLARE v_mov_data DATE;
DECLARE v_mov_descrizione TEXT;
DECLARE v_transazione_id INT;
DECLARE v_confidence DECIMAL(3,2);
DECLARE cur_movimenti CURSOR FOR
SELECT id, importo, data_movimento, descrizione
FROM movimenti_bancari
WHERE conto_bancario_id = p_conto_bancario_id
AND data_movimento BETWEEN p_periodo_da AND p_periodo_a
AND riconciliato = FALSE;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET p_movimenti_riconciliati = 0;
OPEN cur_movimenti;
riconcilia_loop: LOOP
FETCH cur_movimenti INTO v_mov_bancario_id, v_mov_importo, v_mov_data, v_mov_descrizione;
IF done THEN
LEAVE riconcilia_loop;
END IF;
-- Cerca corrispondenza nelle transazioni contabili
SELECT tc.id, 1.00 INTO v_transazione_id, v_confidence
FROM transazioni_contabili tc
JOIN righe_movimenti_contabili rmc ON tc.id = rmc.transazione_id
JOIN piano_conti pc ON rmc.conto_id = pc.id
JOIN conti_bancari cb ON pc.id = cb.conto_piano_conti_id
WHERE cb.id = p_conto_bancario_id
AND ABS(rmc.importo - ABS(v_mov_importo)) <= p_soglia_matching
AND ABS(DATEDIFF(tc.data_transazione, v_mov_data)) <= 3
AND tc.id NOT IN (
SELECT transazione_contabile_id
FROM movimenti_riconciliazione
WHERE transazione_contabile_id IS NOT NULL
)
ORDER BY ABS(rmc.importo - ABS(v_mov_importo)), ABS(DATEDIFF(tc.data_transazione, v_mov_data))
LIMIT 1;
-- Se trovata corrispondenza, crea record di riconciliazione
IF v_transazione_id IS NOT NULL THEN
INSERT INTO movimenti_riconciliazione (
riconciliazione_id, movimento_bancario_id, transazione_contabile_id,
tipo_matching, confidence_score, validato
) VALUES (
(SELECT id FROM riconciliazioni_bancarie
WHERE conto_bancario_id = p_conto_bancario_id
AND periodo_da = p_periodo_da AND periodo_a = p_periodo_a LIMIT 1),
v_mov_bancario_id, v_transazione_id, 'AUTOMATICO', v_confidence, TRUE
);
UPDATE movimenti_bancari
SET riconciliato = TRUE, riconciliato_at = CURRENT_TIMESTAMP
WHERE id = v_mov_bancario_id;
SET p_movimenti_riconciliati = p_movimenti_riconciliati + 1;
END IF;
SET v_transazione_id = NULL;
END LOOP;
CLOSE cur_movimenti;
END//
DELIMITER ;
```
---
## 🆕 VISTE AVANZATE PER REPORTING
### 📊 Vista Situazione Contabile Generale
```sql
CREATE VIEW v_situazione_contabile AS
SELECT
c.id as condominio_id,
c.denominazione as condominio,
gc.anno_riferimento,
gc.tipo_gestione,
gc.stato as stato_gestione,
-- Totali per tipo conto
SUM(CASE WHEN pc.tipo_conto = 'ATTIVO' THEN pc.saldo_finale ELSE 0 END) as totale_attivo,
SUM(CASE WHEN pc.tipo_conto = 'PASSIVO' THEN pc.saldo_finale ELSE 0 END) as totale_passivo,
SUM(CASE WHEN pc.tipo_conto = 'COSTO' THEN pc.saldo_finale ELSE 0 END) as totale_costi,
SUM(CASE WHEN pc.tipo_conto = 'RICAVO' THEN pc.saldo_finale ELSE 0 END) as totale_ricavi,
-- Indicatori
(SUM(CASE WHEN pc.tipo_conto = 'ATTIVO' THEN pc.saldo_finale ELSE 0 END) -
SUM(CASE WHEN pc.tipo_conto = 'PASSIVO' THEN pc.saldo_finale ELSE 0 END)) as patrimonio_netto,
(SUM(CASE WHEN pc.tipo_conto = 'RICAVO' THEN pc.saldo_finale ELSE 0 END) -
SUM(CASE WHEN pc.tipo_conto = 'COSTO' THEN pc.saldo_finale ELSE 0 END)) as risultato_gestione,
gc.budget_preventivo,
gc.saldo_finale as saldo_effettivo,
-- Liquidità
SUM(CASE WHEN cb.tipo_conto IN ('BANCARIO','POSTALE','CASSA') THEN cb.saldo_attuale ELSE 0 END) as liquidita_totale
FROM condomini c
JOIN gestioni_contabili gc ON c.id = gc.condominio_id
JOIN piano_conti pc ON c.id = pc.condominio_id
LEFT JOIN conti_bancari cb ON c.id = cb.condominio_id AND cb.attivo = TRUE
WHERE pc.attivo = TRUE
GROUP BY c.id, gc.id;
```
### 📊 Vista Scadenzario Completo
```sql
CREATE VIEW v_scadenzario_completo AS
SELECT
'DOCUMENTO' as tipo_scadenza,
dc.id as riferimento_id,
dc.condominio_id,
dc.descrizione_soggetto as descrizione,
dc.data_scadenza,
dc.importo_totale as importo,
dc.stato,
'PAGAMENTO' as azione_richiesta,
DATEDIFF(dc.data_scadenza, CURRENT_DATE) as giorni_scadenza,
CASE
WHEN dc.data_scadenza < CURRENT_DATE THEN 'SCADUTO'
WHEN DATEDIFF(dc.data_scadenza, CURRENT_DATE) <= 7 THEN 'IN_SCADENZA'
ELSE 'OK'
END as urgenza
FROM documenti_contabili dc
WHERE dc.data_scadenza IS NOT NULL
AND dc.stato NOT IN ('PAGATO','INCASSATO','ANNULLATO')
UNION ALL
SELECT
'RITENUTA' as tipo_scadenza,
rf.id as riferimento_id,
rf.condominio_id,
CONCAT('Ritenuta ', rf.mese_competenza, ' - ', f.ragione_sociale) as descrizione,
rf.data_scadenza_versamento as data_scadenza,
rf.importo_ritenuta as importo,
CASE WHEN rf.versata THEN 'VERSATA' ELSE 'DA_VERSARE' END as stato,
'VERSAMENTO_F24' as azione_richiesta,
DATEDIFF(rf.data_scadenza_versamento, CURRENT_DATE) as giorni_scadenza,
CASE
WHEN rf.data_scadenza_versamento < CURRENT_DATE AND NOT rf.versata THEN 'SCADUTO'
WHEN DATEDIFF(rf.data_scadenza_versamento, CURRENT_DATE) <= 3 AND NOT rf.versata THEN 'IN_SCADENZA'
ELSE 'OK'
END as urgenza
FROM ritenute_fiscali rf
JOIN fornitori f ON rf.fornitore_id = f.id
WHERE rf.versata = FALSE
ORDER BY data_scadenza ASC;
```
---
## 🚀 IMPLEMENTAZIONE LARAVEL
### 🎨 Form Unico di Registrazione
Il sistema implementa una maschera unica che si adatta dinamicamente al tipo di documento:
```php
// Controller per maschera unica
class DocumentoContabileController extends Controller
{
public function create(Request $request)
{
$tipoDocumento = $request->get('tipo', 'FATTURA_PASSIVA');
$gestioni = GestioneContabile::where('condominio_id', auth()->user()->condominio_id)
->where('stato', 'APERTA')->get();
$fornitori = Fornitore::where('condominio_id', auth()->user()->condominio_id)->get();
$tabelleMillesimali = TabellaMillesimale::where('condominio_id', auth()->user()->condominio_id)
->where('attiva', true)->get();
return view('contabilita.documenti.create', compact(
'tipoDocumento', 'gestioni', 'fornitori', 'tabelleMillesimali'
));
}
public function store(StoreDocumentoRequest $request)
{
DB::transaction(function() use ($request) {
// Crea documento
$documento = DocumentoContabile::create($request->validated());
// Se ripartizione automatica abilitata, eseguila
if ($request->ripartizione_automatica) {
$this->eseguiRipartizioneAutomatica($documento);
}
// Crea movimenti contabili se stato = VALIDATO
if ($request->stato === 'VALIDATO') {
$this->creaMovimentiContabili($documento);
}
});
return redirect()->route('contabilita.documenti.index')
->with('success', 'Documento registrato con successo');
}
}
```
### 🏦 Dashboard Contabile
```php
class ContabilitaController extends Controller
{
public function dashboard()
{
$condominioId = auth()->user()->condominio_id;
$situazioneContabile = DB::table('v_situazione_contabile')
->where('condominio_id', $condominioId)
->first();
$scadenzario = DB::table('v_scadenzario_completo')
->where('condominio_id', $condominioId)
->where('urgenza', '!=', 'OK')
->orderBy('giorni_scadenza')
->limit(10)
->get();
$liquidita = ContoBancario::where('condominio_id', $condominioId)
->where('attivo', true)
->sum('saldo_attuale');
return view('contabilita.dashboard', compact(
'situazioneContabile', 'scadenzario', 'liquidita'
));
}
}
```
---
## 📱 ESEMPI PRATICI WORKFLOW
### 💡 Workflow Completo: Fattura ENEL
```sql
-- 1. Registrazione in maschera unica
INSERT INTO documenti_contabili (
condominio_id, gestione_id, numero_protocollo, tipo_documento,
data_documento, data_registrazione, data_competenza_da, data_competenza_a,
fornitore_id, importo_totale, descrizione_soggetto, ripartizione_automatica,
tabella_millesimale_id, created_by
) VALUES (
1, 1, 'DOC2025/001', 'FATTURA_PASSIVA',
'2025-01-31', '2025-02-01', '2025-01-01', '2025-01-31',
15, 100.00, 'Fattura energia elettrica gennaio', TRUE,
1, 1
);
-- 2. Trigger automatico crea ripartizioni
-- (Eseguito automaticamente al cambio stato in VALIDATO)
-- 3. Creazione transazione contabile automatica
INSERT INTO transazioni_contabili (
condominio_id, gestione_id, documento_id, numero_transazione,
data_transazione, data_competenza, descrizione, importo_totale,
tipo_transazione, created_by
) VALUES (
1, 1, 1, 'TC2025/001', '2025-02-01', '2025-01-31',
'Registrazione fattura ENEL', 100.00, 'REGISTRAZIONE', 1
);
-- 4. Righe contabili partita doppia
INSERT INTO righe_movimenti_contabili (transazione_id, conto_id, tipo_movimento, importo) VALUES
(1, 45, 'DARE', 100.00), -- Costo Energia Elettrica
(1, 78, 'AVERE', 100.00); -- Debito vs ENEL
-- 5. Pagamento successivo
INSERT INTO transazioni_contabili (
condominio_id, gestione_id, numero_transazione,
data_transazione, descrizione, importo_totale, tipo_transazione, created_by
) VALUES (
1, 1, 'TC2025/002', '2025-03-31', 'Pagamento fattura ENEL', 100.00, 'PAGAMENTO', 1
);
INSERT INTO righe_movimenti_contabili (transazione_id, conto_id, tipo_movimento, importo) VALUES
(2, 78, 'DARE', 100.00), -- Chiusura debito ENEL
(2, 12, 'AVERE', 100.00); -- Uscita da C/C bancario
-- 6. Riconciliazione bancaria automatica
CALL sp_riconciliazione_automatica(1, '2025-03-01', '2025-03-31', 0.01, @riconciliati);
```
---
## 🚀 BENEFICI DEL SISTEMA AVANZATO
### ✅ **Vantaggi Operativi**
- **Maschera Unica:** Un solo form per tutti i tipi di documento
- **Ripartizione Automatica:** Calcolo millesimale istantaneo
- **Quadratura Real-Time:** Bilancio sempre controllato
- **Workflow Guidato:** Processo step-by-step assistito
### ✅ **Vantaggi Fiscali**
- **Compliance Automatica:** Adempimenti fiscali automatizzati
- **Scadenzario Intelligente:** Alert preventivi su scadenze
- **F24 Automatici:** Generazione modelli precompilati
- **Controllo Ritenute:** Gestione completa ritenute d'acconto
### ✅ **Vantaggi Audit e Sicurezza**
- **Backup Granulari:** Restore selettivo per condominio
- **Audit Completo:** Tracciabilità totale ogni modifica
- **Riconciliazione Automatica:** Controllo movimenti bancari
- **Integrità Garantita:** Hash e controlli di integrità
### ✅ **Vantaggi Analitici**
- **Reportistica Avanzata:** Viste preconfigurate per analisi
- **Dashboard Real-Time:** Situazione sempre aggiornata
- **KPI Automatici:** Indicatori di performance calcolati
- **Drill-Down Completo:** Da sintesi a dettaglio in un click
---
*Sistema Contabile NetGesCon v3.0*
*Implementazione Avanzata: Gennaio 2025*
*🚀 Ready for Production*