# Mini AI `.202` - riconciliazione banca/incassi ## Obiettivo Usare la macchina `.202` come motore di suggerimenti per riconciliare incassi, movimenti bancari e scritture contabili. La mini AI non deve scrivere sul gestionale: deve restituire proposte controllabili dall'operatore. ## Primo perimetro reale - Stabile: `0016` / `stabile_id=17` - Fonte incassi iniziale: `netgescon.incassi`, importata da `singolo_anno.mdb.incassi` - Fonte movimenti banca: `contabilita_movimenti_banca`, import Fucino validato - Stato `in_da_ec`: opzionale; se vuota, il payload usa automaticamente `netgescon.incassi` ## Configurazione NetGescon Sul nodo applicativo impostare: ```env MINI_AI_ENABLED=true MINI_AI_BASE_URL=http://192.168.0.202:8000 MINI_AI_RECONCILIATION_PATH=/api/v1/reconciliation/incassi MINI_AI_TIMEOUT=30 ``` Se la mini AI espone autenticazione bearer, aggiungere anche: ```env MINI_AI_TOKEN= ``` Poi eseguire: ```bash php artisan config:clear php artisan netgescon:mini-ai-incassi --codice-stabile=0016 --limit=2 --dry-run php artisan netgescon:mini-ai-incassi --codice-stabile=0016 --limit=20 ``` Se la UI `admin-filament/contabilita/incassi` mostra `Mini AI non abilitata`, il controllo locale e: ```bash php artisan tinker --execute='dump(config("services.mini_ai"));' php artisan config:clear ``` La pagina salva comunque la richiesta in `ai_requests`, ma non prova la POST verso `.202` finche `MINI_AI_ENABLED=true` non e attivo. ## Endpoint atteso su `.202` La macchina `.202` deve esporre: ```text POST /api/v1/reconciliation/incassi ``` Input: JSON prodotto da NetGescon con: - `task = reconcile_incassi_movimenti_bancari` - `mode = read_only_suggestions` - `snapshot.meta` - `snapshot.records[]` - `snapshot.records[].payer_reference` quando il bonifico contiene `Coord.Ordinante`, `Banca Ordinante`, `Cro` Output richiesto: ```json { "summary": "string", "warnings": ["string"], "suggestions": [ { "legacy_key": "inc:11172", "confidence": 0.82, "suggested_incasso_id": 11172, "suggested_movimento_id": 15800, "suggested_action": "review", "reason": "Importo coincidente; data lontana, richiede controllo umano.", "allocations": [ { "rate_id": 42230, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "Acconto gestione corrente indicato nella causale." } ] } ] } ``` ## Regole operative HUB incassi - La coda operativa deve mostrare solo movimenti banca interni alla finestra delle gestioni ordinarie aperte. - Per lo stabile `0016` la finestra verificata e `2024/25` + `2025/26`; la UI deve poter comunque spostare la competenza sulla gestione chiusa immediatamente precedente `2023/24` quando la causale banca indica saldi o conguagli dell anno precedente. - I riferimenti `Coord.Ordinante`, `Banca Ordinante`, `BIC/SWIFT` e `CRO` vanno trattati come indizi forti: se l operatore conferma il soggetto corretto, il mapping deve essere memorizzato per accelerare i pagamenti successivi della stessa banca/IBAN. - Nel blocco C/I dell HUB il nominativo operatore deve seguire questo ordine: `unita_immobiliari.denominazione`, poi `gescon_import.condomin.nom_cond`, solo in ultima istanza `soggetti` legacy. - Le spese straordinarie aperte vanno tenute in una sezione separata dalle rate ordinarie e devono restare visibili fino a chiusura effettiva, anche se la coda operativa ordinaria lavora su una finestra di gestioni piu stretta. - Nel riepilogo del C/I il gestionale deve esporre anche i parziali del dovuto per tipo (`ordinarie` / `straordinarie`), per gestione e il totale generale dovuto: servono per solleciti e chiusure contabili in partita doppia. - Se una gestione straordinaria esiste in `gestioni_contabili` / `lavori_straordinari` ma non produce righe residue sull interno, la UI deve segnalarla come possibile buco di import/materializzazione delle spese straordinarie. Azioni ammesse: - `link`: proposta molto forte, comunque da confermare in NetGescon - `review`: caso plausibile ma da verificare - `ignore`: non sembra riconciliabile - `duplicate`: sospetto duplicato o movimento gia coperto ## Prompt da dare all'agent sulla `.202` ```text Sei la mini AI banca/contabilita di NetGescon. Devi esporre un servizio HTTP locale su /api/v1/reconciliation/incassi che riceve snapshot JSON read-only da NetGescon. Regole: - Non scrivere mai sul gestionale. - Non chiamare API di update. - Restituisci solo suggerimenti JSON. - Usa importo, data, descrizione, nominativo, scala/interno, ricevuta, protocollo e candidati movimento. - Le spese bancarie ricorrenti, bolli, commissioni e canoni banca vanno classificate come casi facili ma non collegate a incassi condominiali. - I CBILL vanno riconosciuti dal codice/causale quando presente. - Se la confidenza e bassa o ci sono piu candidati simili, usa suggested_action=review. - Se un movimento sembra gia registrato o duplicato, usa suggested_action=duplicate. - Rispondi sempre con JSON valido: summary, warnings, suggestions[]. Schema suggestion: legacy_key, confidence, suggested_incasso_id, suggested_movimento_id, suggested_action, reason, allocations[]. Quando il record contiene `candidate_rates`, devi valorizzare `allocations[]` con il sottoinsieme di rate selezionato. Ogni elemento deve contenere almeno: `rate_id`, `amount`, `ci`, `gestione`, `reason`. Nella UI di NetGescon, le allocazioni della risposta vengono mostrate nel pannello mini AI e possono essere trasferite sulle imputazioni manuali del movimento selezionato con il comando `Applica allocazioni AI alle rate`. La chiusura della riconciliazione resta comunque bloccata alla quadratura esatta tra importo banca e totale imputato. ``` ## Apprendimento progressivo Nella prima fase l'apprendimento avviene salvando: - payload inviato in `ai_requests.request_text` - risposta in `ai_requests.response_text` - feedback operatore nella UI incassi / feedback AI Solo dopo una serie di risposte corrette si potra introdurre una tabella regole o una memoria dedicata per pattern ricorrenti come spese bancarie, CBILL, MAV e bonifici con causali standard. ## Servizio persistente su `.202` con systemd Quando l'endpoint risponde correttamente ai test manuali, renderlo persistente con `systemd`. Esempio di unit, da adattare al percorso reale dell'app mini AI sulla `.202`: ```ini [Unit] Description=NetGescon Mini AI Reconciliation API After=network-online.target Wants=network-online.target [Service] Type=simple User=michele WorkingDirectory=/home/michele/mini-ai-reconciliation Environment=MINI_AI_TOKEN= ExecStart=/home/michele/mini-ai-reconciliation/.venv/bin/uvicorn app:app --host 0.0.0.0 --port 8000 --workers 2 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` Comandi sulla `.202`: ```bash sudo nano /etc/systemd/system/netgescon-mini-ai.service sudo systemctl daemon-reload sudo systemctl enable --now netgescon-mini-ai.service sudo systemctl status netgescon-mini-ai.service journalctl -u netgescon-mini-ai.service -f ``` Se si abilita token bearer, impostare `MINI_AI_TOKEN` nella unit e configurare lo stesso valore sul nodo NetGescon con `MINI_AI_TOKEN=`. Non salvare token nei documenti o in chat. ## Caso guida: Barone Michele, bonifico `1.285,70` Movimento banca reale: - `contabilita_movimenti_banca.id = 15523` - data `2025-01-16` - importo `1.285,70` - descrizione banca: ```text BONIFICO A VOSTRO FAVORE NETHOME DI BARONE MICHELE E C. SOCI Data Regolamento: 16/01/25 Coord.Ordinante: IT85 H010 3003 2710 0000 1540 577 Banca Ordinante: 01030/01697-PASCITMM Cro: A100267492801030480320003200IT Note: SALDO CONDOMINIO ANNO 2024 E ACC.TO 2025 E/C R IF. sc.B int.4 BARONE MICHELE Id.Operazione: 277385815 ``` Comando NetGescon per generare il payload: ```bash php artisan netgescon:mini-ai-incassi --movimento-id=15523 --limit=30 --dry-run > payload-barone-15523.json curl -sS -X POST 'http://192.168.0.202:8000/api/v1/reconciliation/incassi' \ -H 'Content-Type: application/json' \ -d @payload-barone-15523.json ``` Regola attesa: - leggere `sc.B int.4` e `BARONE MICHELE` - attribuire il movimento al ramo `C` se le rate candidate sono `ci=C` - interpretare `SALDO CONDOMINIO ANNO 2024 E ACC.TO 2025` come: - saldo gestione chiusa/adiacente `2023/24` - acconto gestione corrente `2024/25` - non andare su gestioni molto vecchie - non selezionare rate successive al pagamento se esiste una combinazione esatta sulle rate gia emesse alla data del versamento - scegliere la combinazione che torna a `1.285,70` Allocazione attesa per il caso Barone: ```json { "legacy_key": "mov:15523", "confidence": 0.98, "suggested_incasso_id": null, "suggested_movimento_id": 15523, "suggested_action": "link", "reason": "La causale indica saldo condominio anno 2024 e acconto 2025; scala/interno e nominativo coincidono; la somma delle rate selezionate coincide con il bonifico.", "allocations": [ { "rate_id": 41225, "amount": 87.84, "ci": "C", "gestione": "2023/24", "reason": "Conguaglio esercizio precedente, saldo anno 2024." }, { "rate_id": 41457, "amount": 87.84, "ci": "C", "gestione": "2023/24", "reason": "Conguaglio esercizio precedente, saldo anno 2024." }, { "rate_id": 41261, "amount": 87.84, "ci": "C", "gestione": "2023/24", "reason": "Conguaglio esercizio precedente, saldo anno 2024." }, { "rate_id": 41077, "amount": 241.00, "ci": "C", "gestione": "2023/24", "reason": "Rata giugno-luglio 2024 inclusa nel saldo anno 2024." }, { "rate_id": 41517, "amount": 241.00, "ci": "C", "gestione": "2023/24", "reason": "Rata agosto-settembre 2024 inclusa nel saldo anno 2024." }, { "rate_id": 42230, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "Prima rata gestione 2024/25, acconto 2025." }, { "rate_id": 42276, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "Seconda rata gestione 2024/25, acconto 2025." }, { "rate_id": 41668, "amount": 93.09, "ci": "C", "gestione": "2023/24", "reason": "ATTO AeR ricevuto 05/07/24, prima rata." }, { "rate_id": 41597, "amount": 93.09, "ci": "C", "gestione": "2023/24", "reason": "ATTO AeR ricevuto 05/07/24, seconda rata." } ] } ``` Somma attesa `allocations.amount`: `1.285,70`. Nota per l'agent `.202`: nel payload Barone sono presenti anche rate candidate non selezionate. Non vanno scelte se non servono alla quadratura o se sono fuori dalla causale. In particolare, con questa causale non selezionare rate provvisorie vecchie o la `3/6 Rata FEBBRAIO-MARZO 25` se la combinazione sopra quadra gia esattamente. ## Caso guida: Barone Michele, bonifico `960,79` Movimento banca reale: - `contabilita_movimenti_banca.id = 15953` - data `2025-12-23` - importo `960,79` - descrizione banca: ```text BONIFICO A VOSTRO FAVORE NETHOME DI BARONE MICHELE E C. SOCI Data Regolamento: 23/12/25 Coord.Ordinante: IT85 H010 3003 2710 0000 1540 577 Banca Ordinante: 01030/01697-PASCITMM Cro: A108446418101030480320003200IT Note: SALDO CONDOMINIO ANNO 2025 E ACC.TO 2026 E/C R IF. sc.B int.4 BARONE MICHELE Id.Operazione: 304017429 ``` Comando NetGescon per generare il payload: ```bash php artisan netgescon:mini-ai-incassi --movimento-id=15953 --limit=40 --dry-run > payload-barone-15953.json curl -sS -X POST 'http://192.168.0.202:8000/api/v1/reconciliation/incassi' \ -H 'Content-Type: application/json' \ -d @payload-barone-15953.json ``` Regola attesa: - leggere `sc.B int.4` e `BARONE MICHELE` - attribuire il movimento al ramo `C` se le rate candidate sono `ci=C` - interpretare `SALDO CONDOMINIO ANNO 2025 E ACC.TO 2026` come: - saldo gestione `2024/25` - acconto gestione `2025/26` - includere i conguagli negativi se servono alla quadratura - non selezionare rate successive non necessarie se esiste una combinazione esatta - scegliere la combinazione che torna a `960,79` Allocazione attesa: ```json { "legacy_key": "mov:15953", "confidence": 0.98, "suggested_incasso_id": null, "suggested_movimento_id": 15953, "suggested_action": "link", "reason": "La causale indica saldo condominio anno 2025 e acconto 2026; scala/interno e nominativo coincidono; la somma delle rate selezionate coincide con il bonifico.", "allocations": [ { "rate_id": 41878, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "3/6 rata gestione 2024/25, saldo anno 2025." }, { "rate_id": 42355, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "4/6 rata gestione 2024/25, saldo anno 2025." }, { "rate_id": 42546, "amount": -0.61, "ci": "C", "gestione": "2024/25", "reason": "Conguaglio negativo da includere nella quadratura." }, { "rate_id": 42468, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "5/6 rata gestione 2024/25, saldo anno 2025." }, { "rate_id": 42706, "amount": 177.00, "ci": "C", "gestione": "2024/25", "reason": "6/6 rata gestione 2024/25, saldo anno 2025." }, { "rate_id": 42803, "amount": 177.00, "ci": "C", "gestione": "2025/26", "reason": "Prima rata gestione 2025/26, acconto 2026." }, { "rate_id": 42134, "amount": 38.20, "ci": "C", "gestione": "2024/25", "reason": "Lav. facciata lato parrocchia, prima rata capitolato." }, { "rate_id": 42194, "amount": 38.20, "ci": "C", "gestione": "2024/25", "reason": "Lav. facciata lato parrocchia, seconda rata capitolato." } ] } ``` Somma attesa `allocations.amount`: `960,79`. Nota per l'agent `.202`: nel payload `mov:15953` sono presenti anche rate candidate non selezionate, come `1/6` e `2/6` 2024/25 o rate successive 2025/26. Non vanno scelte se il saldo indicato dalla causale quadra gia con la combinazione sopra. ## BUG risolto nel primo collaudo Il payload da 20 righe supera `64 KB`; `ai_requests.request_text` e `response_text` devono essere `LONGTEXT`. La migration `2026_06_07_130000_expand_ai_requests_text_columns.php` applica questa correzione. ## Collaudo end-to-end 07/06/2026 Endpoint `.202` verificato: ```text POST http://192.168.0.202:8000/api/v1/reconciliation/incassi ``` Esito con payload di esempio fornito dall'agent `.202`: - HTTP `200` - risposta valida con `summary`, `warnings`, `suggestions[]` - match forte su esempio `inc:11172 -> movimento 15800`, `confidence=0.99`, `suggested_action=link` Esito con payload reale prodotto da NetGescon: ```bash MINI_AI_ENABLED=true MINI_AI_BASE_URL=http://192.168.0.202:8000 MINI_AI_RECONCILIATION_PATH=/api/v1/reconciliation/incassi php artisan netgescon:mini-ai-incassi --codice-stabile=0016 --limit=2 ``` - `ai_request_id=2` - risposta ricevuta e salvata in `ai_requests` - `status=answered` - `request_len=4600` - `response_len=734` - risposta: `2 record analizzati; 2 review` - suggerimenti su `inc:11172 -> movimento 15800` e `inc:11170 -> movimento 15822`, entrambi `confidence=0.45` e `suggested_action=review` Interpretazione: il collegamento funziona. La qualita del suggerimento e volutamente prudente per i primi dati reali, perche i candidati hanno importi uguali ma date distanti o ambigue. Esito successivo con payload movimento Barone: - comando: `php artisan netgescon:mini-ai-incassi --movimento-id=15523 --limit=30` - richiesta generata: `legacy_key=mov:15523`, `candidate_rates=14` - risposta `.202` attuale: link al movimento con `confidence=0.99`, ma senza `allocations[]` - prossimo aggiornamento richiesto all'agent `.202`: usare `candidate_rates` e restituire la scomposizione rate nel formato indicato sopra. Esito con payload movimento Barone `960,79`: - comando: `php artisan netgescon:mini-ai-incassi --movimento-id=15953 --limit=40` - richiesta generata: `legacy_key=mov:15953`, `candidate_rates=12` - risposta `.202` attuale: link al movimento con `confidence=0.99`, ma `allocations=[]` - prossimo aggiornamento richiesto all'agent `.202`: includere conguagli negativi e selezionare il sottoinsieme esatto da `960,79`. ## Caso guida: Caschetto Lucia / Curatola Antonio, bonifico `1.968,23` Movimento banca reale: - `contabilita_movimenti_banca.id = 15803` - data `2026-04-30` - importo `1.968,23` - descrizione banca: ```text BONIFICO A VOSTRO FAVORE CURATOLA ANTONIO Data Regolamento: 30/04/26 Coord.Ordinante: IT57 F020 0805 2120 0000 3789 819 Banca Ordinante: 01020/02080-UNCRITMM Cro: 1101261190461826 Note: estratto conto condominiale Germanico 96 del 0 8.04.2026 Id.Operazione: NOTPROVIDED ``` Particolarita anagrafica: - il bonifico non indica `B/18` - il nominativo banca e `Curatola Antonio` - nel legacy l'unita corretta e `B/18 - Caschetto Lucia` - `gescon_import.condomin.presso = c/o Avv. Curatola` - `gescon_import.condomin.e_mail_condomino` contiene `curatola.antonio@libero.it,lcaschetto@gmail.com` Regola attesa: - usare `presso` ed email come alias di riconoscimento, senza cambiare il nominativo principale della unita - mantenere `Caschetto Lucia` come intestatario principale - usare le rate candidate `ci=C` - scegliere tutte le 12 rate candidate perche la somma coincide con `1.968,23` Esito test NetGescon: ```bash php artisan netgescon:mini-ai-incassi --movimento-id=15803 --limit=40 --dry-run ``` - `legacy_key=mov:15803` - `nominativo=Caschetto Lucia` - `scala=B`, `interno=18` - `candidate_rates=12` - somma candidate `1.968,23` Esito `.202` dopo aggiornamento agent: - `ai_request_id=5` - `confidence=0.98` - `suggested_action=link` - `allocations[]` valorizzato con le 12 rate candidate - risposta corretta per il primo caso con alias `presso/email`