📋 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
31 KiB
31 KiB
🏦 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)
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
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
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
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
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
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
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
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
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
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:
// 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
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
-- 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