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

31 KiB
Raw Permalink Blame History

🏦 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