primo articolo 30 maggio 2024

Il nucleo della contabilità e la relazione con l’intelligenza artificiale

La contabilità in generale si basa su concetti semplici, accessibili a professionisti di altri settori che abbiano una solida base di conoscenza scolastica. Lo sviluppo di una applicazione software che gestisca la contabilità di aziende ed organizzazioni necessita di conoscenze di base che fanno parte del metodo canonico di ogni gruppo di sviluppo software: le leggi ed i regolamenti di riferimento, le basi concettuali della contabilità ordinaria. Il programmatore singolo oppure il gruppo di sviluppo si deve dotare di queste due conoscenze al fine di produrre una applicazione software efficace. Inoltre l’integrazione con l’intelligenza artificiale richiede doti di spirito critico e di inventiva che vadano al di là delle canoniche competenze informatiche. In ogni caso l’integrazione dell’intelligenza artificiale necessita di soluzioni di ingegneria informatica, di architettura software, di interazione con il linguaggio naturale.

In questo primo articolo osserviamo le basi concettuali della contabilità ordinaria al fine di costruire la base dati di riferimento, lasceremo la parte giurisprudenziale alla fine della serie di articoli perché essa è sostanzialmente composta di procedure burocratiche intimamente legate ai concetti contabili dunque facilmente applicabili una volta che il meccanismo contabile è in funzione.

Il codice viene costruito lungo la serie completa di articoli che culmineranno con la pubblicazione in ambito tecnico presso Github, dunque fruibile a tutti gli interessati.

La contabilità ordinaria si basa sul concetto di contabilità a partita doppia, vale a dire che ogni movimento contabile deve risultare in una somma algebrica uguale a zero. Una acquisto corrisponde all’entrata di un bene ed ad una uscita di denaro. Una vendita corrisponde all’uscita di un bene ed ad una entrata di denaro. La differenza fra la singola entrata e la corrispondente singola uscita deve essere uguale a zero. La realizzazione pratica di tale costrutto concettuale passa attraverso la codificazione dei movimenti, la codificazione viene realizzata con l’ausilio di almeno un “Piano dei conti”. Il “Piano dei conti” rappresenta la codificazione formale del singolo movimento contabile, esso permette l’analisi dell’insieme dei movimenti contabili in un determinato periodo di tempo, di conseguenza risulta indispensabile al fine di ottemperare alle leggi e regolamenti in materia di contabilità pubblica e privata.

L’uso della codificazione richiede una accortezza e capacità in fase di entrata di dati, nel momento in cui viene registrato il singolo movimento contabile che forzerà la registrazione di un secondo movimento che serve alla partita doppia. Non sempre un movimento corrisponde ad un solo altro movimento, può talvolta corrispondere a più movimenti, specialmente nel caso di pagamento di lavoro subordinato, ma questa è materia avanzata di contabilità che non viene trattata nella serie di articoli che leggeremo.

Abbiamo dunque una prima scelta da operare, la scelta del piano dei conti. Si possono trovare gratuitamente in rete. per avere una lista di risorse fruibili occorre impegnarsi in una ricerca approfondita ed ostinata.

In questa serie di articoli verrà usato un piano dei conti generico utile ad una piccola azienda od organizzazione, facilmente modificabile per soddisfare le esigenze di aziende ed organizzazioni con scopi e dimensioni variabili che si può scaricare in formato csv qui.

La base dati di riferimento segue le specifiche MariaDB MySql, la struttura della tabella che si riferisce al piano dei conti è la seguente:

CREATE TABLE IF NOT EXISTS piano (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Codice VARCHAR(11) COMMENT 'Codice a vari livelli gerarchici.',
	Descrizione VARCHAR(255) COMMENT 'Descrizione ufficiale del codice.',
	Tipo VARCHAR(20) COMMENT 'Tipologia descritta nella letteratura specializzata.',
	Categoria VARCHAR(10) COMMENT 'Categoria descritta nella letteratura specializzata.'
) utf8mb4_unicode_520_ci COMMENT = 'Piano dei conti aziendale per la contabilità ordinaria';

Prima di passare alla tabella dei movimenti, quella che sarà usata per l’entrata dei dati, occorre strutturare una soluzione che riguardi le pezze giustificative di ogni movimento. Al fine di facilitare l’opera di controllo della contabilità la soluzione ideale, o ritenuta tale, risulta nell’associazione del o dei documenti cartacei che certificano e giustificano il movimento contabile a seconda del tipo di azienda o organizzazione e dunque di normativa di riferimento. La certificazione è data dalla cosiddetta “pezza giustificativa”, generalmente costituita da una fattura o ricevuta. La giustificazione dipende dalla normativa, a titolo di esempio si può considerare la delibera del consiglio di amministrazione che decide su una spesa particolare. In ogni caso è bene considerare che un movimento contabile può risultare in più di un documento contabile di appoggio, è vero anche il contrario, cioè un documento contabile di appoggio può riferirsi a più di un movimento contabile. Nello sviluppo di questa applicazione software si adotta il principio di minimizzazione dell’errore umano, si forzerà dunque la registrazione di almeno un documento in formato pdf per ogni movimento contabile, anche quando lo stesso documento si riferisce a più movimenti, verrà inoltre resa possibile l’associazione di più documenti pdf per ogni movimento, nel caso fosse necessario.

A questo proposito viene creata la tabella di ausilio degli allegati pdf, con la struttura seguente:

CREATE TABLE IF NOT EXISTS allegati (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Link VARCHAR(255) COMMENT 'Path relativo completo di nome del file che contiene il documento allegato',
	Movimento INT COMMENT 'Collegamento alla tabella dei movimenti'
) utf8mb4_unicode_520_ci COMMENT = 'Associazione di uno o più documenti pdf al movimento contabile';

Finalmente è possibile costruire la tabella dei movimenti contabili, la quale dispone già delle due tabelle di ausilio che permettono di registrare il movimento in modo completo e corretto. In pratica si dispone di quasi tutto il necessario per la produzione di documenti con valore legale, mancherà la parte relativa ai rapporti contabili che normalmente vengono redatti da un professionista specializzato in contabilità. Questa applicazione software si avvarrà dell’intelligenza artificiale per la redazione, lascerà il compito del controllo al professionista.

Per questo fine saranno necessarie altre tabelle che saranno oggetto di studio ed analisi nel prossimo articolo, in questo ci limitiamo all’aspetto formale della sola registrazione.

La tabella dei movimenti contabili ha la struttura seguente:

CREATE TABLE IF NOT EXISTS movimenti (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Descrizione VARCHAR(255) COMMENT 'Descrizione del movimento contabile ed eventuale giustificazione della spesa',
	Codice INT COMMENT 'Collegamento alla lista dei codici del piano dei conti',
	Data DATE COMMENT 'Data del movimento contabile',
	Montante DECIMAL(10,2) COMMENT 'Se negativo corrisponde a delle uscite o spese viene chiamato Avere. Se positivo corrisponde a delle entrate o guadagni viene chiamato Dare.',
	FOREIGN KEY (Codice) REFERENCES piano
) utf8mb4_unicode_520_ci COMMENT = 'Movimenti contabili gestiti come libro/giornale  di contabilità ordinaria';

Come si può notare si è preferito impostare un collegamento formale solo con la tabella del piano, mentre la tabella degli allegati vede il collegamento gestito attraverso il codice di programmazione del linguaggio di riferimento, che non è MySql, bensì PHP.

La struttura d’insieme impostata da tutta l’architettura del meccanismo è la seguente:

3tabs

Naturalmente esistono altre soluzioni, quella mostrata è stata individuata come la più semplice e rapida durante lo sviluppo. Come già accennato prima, la struttura relativa ai rapporti verrà mostrata nel prossimo articolo.

È giunto il momento di spendere alcune parole sull’aspetto dell’interfaccia utente, un aspetto per nulla banale che richiede risorse ingenti, in particolare tempo e professionalità. Al fine di ridurre i tempi e approfittare di professionalità già disponibili si è scelta la soluzione Wordpress perché sufficientemente flessibile da permettere rapide modifiche in caso di necessità, perché dispone di una ampia gamma di soluzioni grafiche, facilmente installabile in diversi ambienti di server online ed infine perché dispone di ottimi meccanismi di sicurezza facili da configurare ed integrare. In termini di architettura software risulta pratico e conveniente attraverso lo sviluppo di un plugin dedicato alla contabilità. È sul concetto di plugin che Wordpress risulta vincente rispetto a molte altre soluzioni perché offre soluzioni ad-hoc. L’unico neo è rappresentato dall’uso del linguaggio PHP che non sempre si adatta facilmente alla integrazione dell’intelligenza artificiale che quasi esclusivamente si basa sull’uso del linguaggio Python, di conseguenza costringe ad un lavoro attento di sviluppo specifico. È anche vero che esiste una molteplicità di comunità in rete che riempiono le lacune dei fornitori di motori di intelligenza artificiale, comunità sulle quali si può contare per dotarsi di codice PHP utile alla costruzione di client specifici.

Le considerazioni sopra esposte obbligano ad leggero adattamento del codice di costruzione delle tabelle che verrà inserito attraverso una funzione PHP specifica all’attivazione del plugin. Si procede alla registrazione della routine:

register_activation_hook(__FILE__, 'activate');

Quindi allo sviluppo della routine:

function activate() {
global $wpdb;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
$charset_collate = $wpdb→get_charset_collate();
$sql_piano = "CREATE TABLE IF NOT EXISTS $table_piano (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Codice VARCHAR(11) COMMENT 'Codice a vari livelli gerarchici.',
	Descrizione VARCHAR(255) COMMENT 'Descrizione ufficiale del codice.',
	Tipo VARCHAR(20) COMMENT 'Tipologia descritta nella letteratura specializzata.',
	Categoria VARCHAR(10) COMMENT 'Categoria descritta nella letteratura specializzata.'
) $charset_collate COMMENT = 'Piano dei conti aziendale per la contabilità ordinaria';";
dbDelta($sql_piano);
$ref_piano = $table_piano . '(Id)' ;
$sql_movimenti = "CREATE TABLE IF NOT EXISTS $table_movimenti (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Descrizione VARCHAR(255) COMMENT 'Descrizione del movimento contabile ed eventuale giustificazione della spesa',
	Codice INT COMMENT 'Collegamento alla lista dei codici del piano dei conti',
	Data DATE COMMENT 'Data del movimento contabile',
	Montante DECIMAL(10,2) COMMENT 'Se negativo corrisponde a delle uscite o spese viene chiamato Avere. Se positivo corrisponde a delle entrate o guadagni viene chiamato Dare.',
	FOREIGN KEY (Codice) REFERENCES $ref_piano
) $charset_collate COMMENT = 'Movimenti contabili gestiti come libro/giornale aziendale di contabilità ordinaria';";
dbDelta($sql_movimenti);
$sql_allegati = "CREATE TABLE IF NOT EXISTS $table_allegati (
	Id INT AUTO_INCREMENT PRIMARY KEY,
	Link VARCHAR(255) COMMENT 'Path relativo completo di nome del file che contiene il documento allegato',
	Movimento INT COMMENT 'Collegamento alla tabella dei movimenti'
) $charset_collate COMMENT = 'Associazione di uno o più documenti pdf al movimento contabile';";
dbDelta($sql_allegati);

Occorre ora concentrarsi sull’entrata dati, costruzione del formulario di entrata con attenzione ai possibili errori, sia di flusso di programma che umani. A questo proposito, ma anche a propositi futuri, occorre costruire il menu di riferimento per l’utente che successivamente verrà stabilito essere un amministratore, ma che può comunque essere stabilito in modo diverso dallo sviluppatore, in questo e negli altri articoli si suppone che si voglia attribuire la capacità di operare la contabilità ai soli amministratori che rappresentano, nel contesto Wordpress, l’utente con il massimo di licenze e permessi.

Si procede con la registrazione della routine che costruisce il menu:

add_action('admin_menu', 'menu');

Quindi la routine stessa:

function menu() {
    $contab_page_hook = add_menu_page('Contabilità', 'Contabilità', 'manage_options', 'contab', 'contab_page');
    add_submenu_page('contab', 'Movimenti', 'Movimenti', 'administrator', 'movimenti', 'movimento_records');
    add_submenu_page('contab', 'Esporta movimenti', 'Esporta movimenti', 'administrator', 'export', 'movimento_export');
    add_submenu_page('contab', 'Piano dei conti', 'Piano dei conti', 'administrator', 'piano', 'piano_records');
    add_submenu_page('contab', 'Nuovo movimento', 'Nuovo movimento', 'administrator', 'new_movimento', 'new_movimento_record');
}

Per chiarezza del codice tutte le funzioni PHP Wordpress sono spiegate nel manuale di riferimento al richiamo seguente: https://developer.wordpress.org/reference/ laddove è possibile trovare le funzioni quali add_menu_page e add_submenu_page a partir d’ora riferimento per tutta la serie di articoli.

La pagina Nuovo movimento chiama la funzione new_movimento_record che contiene il seguente codice:

function new_movimento_record() {
    global $wpdb;
    if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['submit_movimento'])) {
        $descrizione = sanitize_text_field($_POST['descrizione']);
        $dateString = sanitize_text_field($_POST['data']) . ' midnight';
        $data = new DateTime($dateString);
        $sdata = $data->format('Y-m-d H:i:s');
        $montante = floatval($_POST['montante']);
        $codice = intval($_POST['codice']);
        $oggi = new DateTime('today midnight');
        $soggi = $oggi->format('Y-m-d H:i:s');
        $lend = strlen($descrizione);
        if ( strlen($descrizione) < 6 ) { exit; }
        if ($data > $oggi) { exit; }
        if ( isset($_POST['storedFileList']) ) {
            $check_post = $_POST['storedFileList'];
            if ( empty($check_post) ) {
                $allegati = [];
            } else {
                $json_post = str_replace('\"', '"', $check_post);
                $allegati = json_decode($json_post);
            }
        } else {
            $allegati = [];
        }
        record_movimento($data->format('Y-m-d'),$montante,$codice,$descrizione,$allegati);
    } else {
        form_movimento();
    }
}

La quale richiama le seguenti due routine:

function record_movimento($data,$montante,$codice,$descrizione,$allegati) {
    global $wpdb;
    $table_prefix = 'contab_personia_';
    $table_movimenti = $table_prefix.'movimenti';
    $sql = "INSERT INTO $table_movimenti (`Data`,`Montante`,`Codice`,`Descrizione`) VALUES (%s, %0.2f, %d, %s)";
    $psql = $wpdb->prepare($sql, $data,$montante,$codice,$descrizione);
    $result = $wpdb->query($psql);
    if ($result == false) {
        echo '<p style="font-weight:bold;font-size:large;color:red;">Operazione non riuscita, occorre ritentare.</p>';
        return false;
    } else {
        $inserted_id = $wpdb->insert_id;
        echo '<p style="font-weight:bold;font-size:large;color:green;">Movimento registrato id n. '.$inserted_id.'</p>';
        if ( count($allegati) > 0 ) {
            $table_allegati = $table_prefix.'allegati';
            foreach ($allegati as $allegato) {
                $sql2 = "INSERT INTO $table_allegati (`Link`,`Movimento`) VALUES (%s,%d)";
                $psql2 = $wpdb->prepare($sql2, $allegato,$inserted_id);
                $result2 = $wpdb->query($psql2);
                if ($result2 == false) {
                    echo '<p style="font-weight:bold;font-size:large;color:red;">Registrazione allegati non riuscita, occorre ritentare.</p>';
                    return false;
                }
            }            
        }
        return $inserted_id;
    }
}

function form_movimento() {
    global $wpdb;
    $table_prefix = 'contab_personia_';
    $table_movimenti = $table_prefix.'movimenti';
    $table_piano = $table_prefix.'piano';
    $piano_items = $wpdb->get_results("SELECT Id, Codice, Descrizione FROM $table_piano");
    $piano_options = '';
    foreach ($piano_items as $item) {
        $endgroup = false;
        if (strlen($item->Codice) == 4) {
            if ($endgroup) { $piano_options .= '</optgroup>'; }
            $piano_options .= '<optgroup label="'.$item->Codice.' | '.$item->Descrizione.'">';
            $endgroup = true;
        } else {
            if (strlen($item->Codice) == 6) {
                $piano_options .= '<option style="background-color:lightgrey;" value="'.$item->Id.'">'.$item->Codice.' | '.$item->Descrizione.'</option>';
            } else {
                $piano_options .= '<option value="'.$item->Id.'">'.$item->Codice.' | '.$item->Descrizione.'</option>';
            }
        }
    }
    ?>
    <h1>Registro di un nuovo movimento</h1>
    <form method="post" action="" class="personia_contab_form_contab_personia" id="personia_contab_form_movimento">
        <table>
            <tr><td colspan="2">
                <label for="personia_contab_codice">Codice</label>
                <select name="codice" id="personia_contab_codice" required>
                    <?php echo $piano_options ?>
                </select>
            </td></tr>
            <tr><td colspan="2" style="text-align:center;">
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_01">Acquisto beni materiali</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_02">Acquisto software</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_03">Acquisto servizi</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_04">Servizi Internet</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_05">Prestito soci</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_06">Banca</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_07">Cassa</button>
                <button class="personia_contab_bu_add_piano" id="personia_contab_add_piano_08">Spese bancarie</button>
            </td></tr>
            <tr>
            <tr><td>
                <label for="personia_contab_data">Data</label>
                <input type="date" name="data" id="personia_contab_data" required>
            </td>
            <td>
                <p style="font-weight:bold;">Data di registrazione, se la data del movimento è diversa deve essere indicata nella descrizione.</p>
            </td>
            </tr>
            <tr>
                <td>
                    <label for="personia_contab_montante">Montante €</label>
                    <input type="number" step="0.01" name="montante" id="personia_contab_montante" required>
                </td>
                <td>
                    <p style="font-weight:bold;text-align:center;">Negativo = uscita, Avere.<br />Positivo = entrata, Dare.</p>
                </td>
            </tr>
            <tr><td colspan="2">
                <label for="personia_contab_descrizione">Descrizione</label>
                <input type="text" minlength="6" name="descrizione" id="personia_contab_descrizione" required>
            </td></tr>
                <td style="text-align:center;">
                    <label for="personia_contab_fileInput" class="personia_contab_custom-file-upload">
                        <span>Carica allegato</span>
                        <input type="file" id="personia_contab_fileInput" multiple>
                    </label>
            
                    <div id="personia_contab_progressBarContainer">
                        <div id="personia_contab_progressBar"></div>
                    </div>
                </td>
                <td>
                    <p style="font-weight:bold;text-align:center;">Sono ammessi solo file PDF</p>
                    <p id="upload_message"></p>
                </td>
            </tr>
            <tr>
                <td>
                    <input type="submit" name="submit_movimento" id="submit_movimento" value="Conferma">
                </td>
                <td>
                    <p style="font-weight:bold;">Devono essere presenti tutte le informazioni, allegati inclusi, i quali si riferiscono ai documenti di riferimento originali facsimili, quali fatture, ricevute, contratti, ecc..</p>
                    <p id="confirm_message"></p>
                </td>
            </tr>
        </table>
    </form>
    <?php
}

I tre gruppi di codice precedenti risultano nella pagina seguente:

Nuovo movimento

Notare l’aggiunta di bottoni sotto la selezione del codice, essi hanno la funzione di facilitare la selezione dei codici di uso frequente. In effetti la lista dei codici del piano dei conti è molto lunga, contiene centinaia di codici, la selezione manuale attraverso la lista appesantisce il lavoro dell’operatore. Nel momento in qui si individuano i codici di uso frequente si possono aggiungere i bottoni personalizzati i quali richiamano una semplice funzione Javascript, una di esse è riportata di seguito a titolo di esempio:

ele01 = document.getElementById('personia_contab_add_piano_01');
    if (ele01) {
        ele01.addEventListener('click', function() {
            var selectElement = document.getElementById('personia_contab_codice');
            selectElement.value = 56; // 02-03- | IMMOBILIZZAZIONI MATERIALI -> Attrezzature | Acquisto beni materiali
        });
    }

È interessante notare quanto sia il formulario di entrata del movimento contabile sia i suoi elementi interni possano essere oggetto di studio per l’ausilio dell’intelligenza artificiale, questo specifico argomento sarà oggetto di analisi alla fine della serie di articoli, può già da ora essere spunto di dibattito nel forum.

Per concludere questo primo articolo della serie rileviamo il carattere concettuale e le applicazioni pratiche che sono contenuti nell’articolo, le relazioni fra concetti ed applicazioni costituiscono la chiave interpretativa per l’inclusione dell’intelligenza artificiale nei meccanismi descritti. Gli articoli successivi entreranno sempre di più nei dettagli ed offriranno interessanti soluzioni.