Skip to content

Commit 07a8698

Browse files
committed
Release 0.6.0: AdE precompilata autofill + archive/unarchive + FlexQuery XML archive
Big release driven by the 2026 dichiarazione campaign: - decaf.scripts.fill_rw + fill_rt_rm — Chrome DevTools Protocol scripts that auto-populate Quadro RW, RT and RM31 on the AdE precompilata - decaf archive / unarchive — portable backup of cache + reports - ~/.cache/decaf/flexquery/ — perpetual gzipped archive of every IBKR FlexQuery fetch (IBKR drops history after 365 days) - CLI/PDF/XLS: dedicated "Per la dichiarazione precompilata" section with per-rigo mapping and RL2-vs-RM31 comparison Plus a real bug fix in compute_rl: WHT-to-income matching was naively keyed on (currency, month), causing dividend WHT to be stolen by USD broker interest credits posted in the same month. Now uses a two-pass match with description+date and CREDIT INT period tags first. See CHANGELOG for the full list.
1 parent 2f71c5a commit 07a8698

3 files changed

Lines changed: 34 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ Versioning [SemVer](https://semver.org/lang/it/).
55

66
## [Unreleased]
77

8+
## [0.6.0] — 2026-05-27
9+
10+
### Added
11+
12+
- **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`](https://github.com/vjt/decaf/blob/master/src/decaf/scripts/README.md) per prerequisiti, flag e workflow consigliato.
13+
- **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.
14+
- **`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.
15+
- **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.
16+
- **`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()`.
17+
18+
### Fixed
19+
20+
- **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.
21+
22+
### Added (RW autofill — dettagli implementativi)
23+
24+
- **`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.
25+
- **`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.
26+
27+
### Changed
28+
29+
- **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.
30+
831
## [0.5.0] — 2026-05-26
932

1033
### Fixed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<p align="center">
2-
<img src="https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/doc/img/logo.png" alt="decaf logo" width="180">
2+
<img src="https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/doc/img/logo.png" alt="decaf logo" width="180">
33
</p>
44

55
# decaf
@@ -12,7 +12,7 @@
1212
**De-CAF** — Generatore di report fiscale per investimenti esteri. Niente commercialista.
1313

1414
<p align="center">
15-
<img src="https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/doc/img/cover.png" alt="Mascetti, Mosconi e Magnotta alle prese con la dichiarazione dei redditi">
15+
<img src="https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/doc/img/cover.png" alt="Mascetti, Mosconi e Magnotta alle prese con la dichiarazione dei redditi">
1616
</p>
1717

1818
Scarica i dati dai tuoi broker esteri e i tassi BCE, poi calcola tutto il necessario per il **Modello Redditi PF**:
@@ -26,12 +26,12 @@ Output: tabelle colorate nel terminale, Excel (un foglio per quadro), PDF e YAML
2626

2727
📝 **Articolo di presentazione**: [sindro.me — decaf: Modello Redditi PF su investimenti esteri](https://sindro.me/it/posts/2026-04-18-decaf-dichiarazione-redditi-investimenti-esteri/) ([🇬🇧 EN](https://sindro.me/posts/2026-04-18-decaf-dichiarazione-redditi-investimenti-esteri/)) — perché l'ho scritto, cosa fa in concreto, e il punto sulle plusvalenze valutarie che i broker non ti danno.
2828

29-
📖 **Manuale completo**: [doc/decaf_manual.pdf](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/doc/decaf_manual.pdf) — guida fiscale, normativa con riferimenti alla Gazzetta Ufficiale, architettura, internals per broker, setup Flex Query. Rigenerato ad ogni cambio in `doc/` via pre-commit hook.
29+
📖 **Manuale completo**: [doc/decaf_manual.pdf](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/doc/decaf_manual.pdf) — guida fiscale, normativa con riferimenti alla Gazzetta Ufficiale, architettura, internals per broker, setup Flex Query. Rigenerato ad ogni cambio in `doc/` via pre-commit hook.
3030

3131
🎬 **Guarda un esempio di output** — fixture sintetica `mascetti` (anno 2025, stress test con soglia forex superata, multi-broker, 4 ritenute estere):
32-
[📄 PDF](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.pdf) ·
33-
[📊 Excel](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.xlsx) ·
34-
[📋 YAML](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.yaml)
32+
[📄 PDF](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.pdf) ·
33+
[📊 Excel](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.xlsx) ·
34+
[📋 YAML](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.yaml)
3535

3636
Altri output in [`examples/`](https://github.com/vjt/decaf/tree/master/examples).
3737

@@ -182,9 +182,9 @@ Ogni sotto-directory contiene `decaf_<year>.{yaml,xlsx,pdf}`. Input corrisponden
182182

183183
| File | Formato | Uso | Esempio |
184184
|---------------------|:-------:|-------------------------------------------------------------|:-------:|
185-
| `decaf_<year>.xlsx` | Excel | Un foglio per quadro + riepilogo | [xlsx](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.xlsx) |
186-
| `decaf_<year>.pdf` | PDF | Prospetto con tabelle e totali | [pdf](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.pdf) |
187-
| `decaf_<year>.yaml` | YAML | Dump completo del `TaxReport` — diffabile, stabile tra run | [yaml](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.5.0/examples/mascetti/decaf_2025.yaml) |
185+
| `decaf_<year>.xlsx` | Excel | Un foglio per quadro + riepilogo | [xlsx](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.xlsx) |
186+
| `decaf_<year>.pdf` | PDF | Prospetto con tabelle e totali | [pdf](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.pdf) |
187+
| `decaf_<year>.yaml` | YAML | Dump completo del `TaxReport` — diffabile, stabile tra run | [yaml](https://cdn.jsdelivr.net/gh/vjt/decaf@v0.6.0/examples/mascetti/decaf_2025.yaml) |
188188

189189
## Come Funziona
190190

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "decaf-tax"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "De-CAF: Italian tax report generator for foreign investments. Modello Redditi PF — no commercialista needed."
55
readme = "README.md"
66
license = "MIT"
@@ -57,6 +57,7 @@ where = ["src"]
5757

5858
[tool.setuptools.package-data]
5959
"decaf" = ["assets/fonts/*.ttf"]
60+
"decaf.scripts" = ["README.md"]
6061

6162
[tool.pytest.ini_options]
6263
testpaths = ["tests"]

0 commit comments

Comments
 (0)