Skip to content

v0.6.0 — AdE precompilata autofill + archive/unarchive + FlexQuery XML archive

Latest

Choose a tag to compare

@vjt vjt released this 28 May 08:05
· 6 commits to master since this release

[0.6.0] — 2026-05-27

Added

  • Autofill della dichiarazione precompilata AdE via Chrome DevTools Protocol. Nuovi script python -m decaf.scripts.fill_rw (Quadro RW — monitoraggio fiscale + IVAFE, un rigo per lotto, 5 righi per Modulo) e python -m decaf.scripts.fill_rt_rm (Quadro RT rigo RT11 col.1/col.2 totali plusvalenze 26% + Quadro RM Sez. II-A rigo RM31, un Modulo di Quadro RM per ogni coppia stato estero + tipo reddito). Pilota un Chrome aperto in --remote-debugging-port=9222, popola i campi della React app AdE bypassando il controllo dei controlled inputs (native value setter + invocazione diretta della prop onChange), naviga tra i Moduli rilevando il fallback URL quando la pagina richiesta non esiste ancora ed eseguendo "Aggiungi modulo + Conferma". Convenzioni di sicurezza: rifiuta di sovrascrivere campi pre-esistenti senza --force, abortisce se compare il banner "Salvataggio non effettuato", non clicca mai "Calcola, stampa e invia" — l'invio finale resta sempre una scelta manuale. Helpers CDP condivisi estratti in decaf.scripts._cdp per riuso. Vedi src/decaf/scripts/README.md per prerequisiti, flag e workflow consigliato.
  • Sezione "Per la dichiarazione precompilata" nei report. CLI, PDF e XLS espongono ora una mappa esplicita decaf → Modello Redditi PF: per ogni rigo/colonna dei quadri RW, RT, RL+RM è indicato il valore EUR e la sua origine nei dati decaf. Confronto fianco-a-fianco tra Opzione A (RL+CE, IRPEF marginale + credito imposta estera) e Opzione B (RM31, sostitutiva 26%) con simulazione del netto pagato in entrambi gli scenari ai tre scaglioni IRPEF (23/35/43%) e break-even aliquota marginale calcolato in automatico — utile per scegliere consapevolmente tra le due vie tassative per i redditi di capitale di fonte estera. Breakdown RM31 per coppia (stato, tipo reddito) con codice paese AdE, importo lordo, imposta sostitutiva, count delle entries aggregate.
  • decaf archive / decaf unarchive. Pacchettizza in un singolo .tgz lo stato decaf perché sia portabile tra macchine o archiviabile come backup fiscale long-term: ~/.cache/decaf/statements.db, ~/.cache/decaf/ecb_rates.db, eventuali sotto-directory di cache (flexquery/), più qualsiasi directory extra passata via --tree (es. --tree private/ per includere file sorgenti broker + report YAML/PDF/XLS generati). decaf unarchive ripristina i DB in ~/.cache/decaf/ e i tree sotto --target-dir (default: cwd) mantenendo i path originali, rifiutando di sovrascrivere file esistenti senza --force. Il tarball include un metadata.yaml con versione decaf, data di creazione, e indice del contenuto.
  • Archivio perpetuo XML FlexQuery IBKR su disco. IBKR retiene solo ~365 giorni di storico delle FlexQuery; ad ogni decaf load riuscito via API, _fetch_from_ibkr salva ora una copia gzippata dell'XML scaricato in ~/.cache/decaf/flexquery/ibkr_<account>_<from>_<to>_<fetched_at>.xml.gz (overridabile via --flex-archive-dir). Il salvataggio avviene prima del parsing, quindi anche un XML che decaf non riesce a ingerire sopravvive su disco e può essere riprovato in seguito con decaf load --file. L'account ID viene estratto con un regex senza richiedere un parse completo (fallback a unknown se assente). decaf archive include automaticamente la sotto-directory flexquery/ nei bundle, quindi il backup completo del proprio storico fiscale è una singola invocazione.
  • src/decaf/country_codes.py: mappa ISO 3166-1 alpha-2 → codice numerico AdE (US=069, IE=040, ...) per i quadri che richiedono il codice ministeriale (RM31 col.2, RW col.4). Esposta come iso_to_ade_country_code().

Fixed

  • Matching ritenute → redditi nel Quadro RL: doppio pass per evitare collisioni cross-security. compute_rl() accoppia ora ogni entry di tipo Withholding con la corrispondente entry di reddito in due passate: (1) strong match — stesso description + stessa date_time (caso dividendi: WHT e dividendo postano lo stesso giorno con il nome del titolo come description), OPPURE stesso tag CREDIT INT FOR <MMM>-<YYYY> (caso interessi broker: la WHT e l'interesse maturato hanno descrizioni parallele del tipo USD CREDIT INT FOR AUG-2025 / WITHHOLDING @ 20% ON CREDIT INT FOR AUG-2025); (2) fallback — stessa valuta + stesso mese, per qualunque WHT non ancora consumata. La logica precedente accoppiava solo per valuta+mese, causando due bug visibili nei broker statement reali: (a) WHT di un dividendo META in trimestre Q1 veniva "rubata" da un credito interessi USD dello stesso mese, lasciando il dividendo con WHT > gross apparente; (b) le quattro WHT trimestrali META si accumulavano tutte sulla prima riga dividendo USA del report. Sei regression test in tests/test_quadro_rl.py coprono i due scenari + casi misti. Effetto sul Modello Redditi: le righe RL e i totali aggregati per RM31 (aggregate_rl_for_rm31) sono ora corretti — chi ha dichiarato anni precedenti con decaf < 0.6.0 e ha dividendi su intermediario estero + interessi sullo stesso conto dovrebbe ri-emettere il report e cross-checkare il Quadro RL/RM rispetto a quanto già dichiarato.

Added (RW autofill — dettagli implementativi)

  • fill_rw field map RPF26. Il Modulo di Quadro RW contiene 5 righi RW1-RW5; ogni rigo ha le colonne 1 (codice titolare = 1 proprietà), 3 (codice investimento), 4 (codice stato AdE numerico, NON ISO), 5 (% possesso = 100), 6 (criterio determinazione valore = 1 valore di mercato — campo richiesto, non server-computed), 7 (valore iniziale EUR), 8 (valore finale EUR), 10 (giorni di possesso). La colonna 30 (IVAFE dovuta) è computata server-side dopo Salva, non va inserita. Il form rifiuta lo 0 esplicito nel valore iniziale con "formato non corretto" — fill_rw usa un fallback a 1 EUR per i lotti con valore iniziale nullo (lotti acquisiti nello stesso anno di vendita). L'addressing dei field name è RW{row:03d}{col:03d} dove row è 1-5 (rigo dentro il Modulo), NON l'indice del lotto: 23 lotti → 5 Moduli (RW/1..RW/5), con righi sparsi nei vari Moduli.
  • fill_rt_rm field map RPF26. Quadro RT Sez. II-A 26% = righi RT11..RT16 (non RT21-RT27 della Sez. III-A partecipazioni qualificate). Un unico rigo RT11 aggregato col col.1 = sum(proceeds_eur) e col.2 = sum(cost_basis_eur) su tutti i rt_lines. Quadro RM Sez. II-A: RM31 è un solo rigo per Modulo — sorgenti diverse (interessi USD Schwab, dividendi META USA, interessi EUR IBKR Irlanda) richiedono Moduli di Quadro RM distinti (RM/1, RM/2, RM/3, ...). Field name = RM031{col:03d} dentro ogni Modulo. La colonna 8 (imposta sostitutiva = lordo × 26%) è inserita arrotondata all'intero come da prassi AdE; importi molto piccoli (≤ 0.49 EUR) cadono sullo 0 che il form rifiuta, decaf li scarta ma può essere che il form richieda comunque la colonna — controlla con Verifica i dati. Il form fa fallback alla /RM/N-1 quando si naviga a /RM/N non ancora esistente: fill_rt_rm rileva la condizione confrontando location.pathname dopo il navigate e, se serve, torna alla /RM/N-1 per cliccare "Aggiungi modulo" + "Conferma" prima di scrivere.

Changed

  • Pre-commit hook allineato al CI. Aggiunto ruff format --check src/ tests/ al pre-commit (.githooks/pre-commit), per allineare le check eseguite localmente prima di un commit a quelle che girano in CI. Prima passava solo ruff check (lint), per cui un drift di formattazione (es. una await cdp.eval_js(...) spezzata su tre righe sotto i 100 char) sfuggiva al commit ma bloccava CI al push.