netgescon-day0/docs/support/MINI-AI-202-RICONCILIAZIONE-RUNBOOK.md

497 lines
18 KiB
Markdown
Executable File

# 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=<token locale della macchina .202>
```
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=<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`