@gabrielerandelli/minus-tracker 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -32
- package/dist/cli/index.js +45 -13
- package/dist/cli/index.js.map +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
[](./LICENSE)
|
|
4
4
|
[](https://www.npmjs.com/package/@gabrielerandelli/minus-tracker)
|
|
5
|
+
[](https://www.npmjs.com/package/@gabrielerandelli/minus-tracker)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://modelcontextprotocol.io)
|
|
8
|
+
|
|
9
|
+
⚠️ **Ausilio al calcolo, non consulenza fiscale** — vedi l'[Avvertenza](#avvertenza).
|
|
10
|
+
⚠️ **Calculation aid only, not tax advice** — see the [Disclaimer](#disclaimer).
|
|
11
|
+
|
|
12
|
+
🤖 **Novità: server MCP** — qualsiasi agente AI può calcolare plusvalenze/minusvalenze italiane direttamente, senza passare dalla CLI.
|
|
13
|
+
🤖 **New: MCP server** — any AI agent can compute Italian capital gains/losses directly, no CLI required.
|
|
14
|
+
|
|
15
|
+
✅ Validato su tutti i 12 scenari FAQ dell'Agenzia delle Entrate · 120 test automatici · zero dipendenze runtime nella libreria core.
|
|
16
|
+
✅ Validated against all 12 Agenzia delle Entrate FAQ scenarios · 120 automated tests · zero runtime dependencies in the core library.
|
|
17
|
+
|
|
18
|
+
_L'unico tool open-source che trasforma un export DEGIRO grezzo direttamente in plusvalenze/minusvalenze corrette secondo l'Agenzia delle Entrate — come CLI, libreria, o strumento MCP richiamabile dal tuo agente AI._
|
|
19
|
+
_The only open-source tool that turns a raw DEGIRO export directly into Agenzia-Entrate-correct capital gains/losses — as a CLI, a library, or an MCP tool your AI agent can call._
|
|
5
20
|
|
|
6
21
|
---
|
|
7
22
|
|
|
@@ -16,8 +31,9 @@ Il tool elabora i dati partendo direttamente dal formato CSV esportato da DEGIRO
|
|
|
16
31
|
- Calcolo di plusvalenze e minusvalenze con gestione dei lotti via **LIFO o FIFO** (configurabile)
|
|
17
32
|
- **Parser integrato** per i file CSV di DEGIRO
|
|
18
33
|
- Gestione **multivaluta** con tassi storici BCE (EUR, USD, GBP, CHF)
|
|
19
|
-
- **Classificazione
|
|
20
|
-
di stato, derivati — con classificazione automatica
|
|
34
|
+
- **Classificazione fiscale degli strumenti finanziari in due categorie** (Bucket A/B) — azioni,
|
|
35
|
+
ETF, titoli di stato, derivati — con classificazione automatica tramite OpenFIGI e sidecar JSON
|
|
36
|
+
persistente
|
|
21
37
|
- **Modello Redditi PF**: genera Quadro RT (redditi diversi) e Quadro RM (redditi di capitale) a
|
|
22
38
|
partire dai lotti calcolati, con riporto delle minusvalenze pregresse (regola dei 4 anni)
|
|
23
39
|
- Suite di test allineata alle **FAQ dell'Agenzia delle Entrate**
|
|
@@ -27,7 +43,7 @@ Il tool elabora i dati partendo direttamente dal formato CSV esportato da DEGIRO
|
|
|
27
43
|
**Novità in v0.8.0:**
|
|
28
44
|
|
|
29
45
|
- **Modalità stateless per `Classifier`**: `classify()` può ora funzionare senza alcun accesso al
|
|
30
|
-
filesystem (nessun sidecar), utile per l'integrazione
|
|
46
|
+
filesystem (nessun sidecar), utile per l'integrazione con agenti e automazioni
|
|
31
47
|
- **Server MCP** (`minus-tracker-mcp`): espone `parse_transactions`, `classify_instruments` e
|
|
32
48
|
`calculate_gains` come tool MCP su stdio, per l'uso diretto da parte di agenti AI — vedi la
|
|
33
49
|
sezione [Server MCP](#server-mcp) più sotto
|
|
@@ -97,16 +113,16 @@ npx @gabrielerandelli/minus-tracker calc trades.csv
|
|
|
97
113
|
|
|
98
114
|
### Utilizzo CLI
|
|
99
115
|
|
|
100
|
-
| Comando | Flag principali | Note
|
|
101
|
-
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
102
|
-
| `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` |
|
|
103
|
-
| `classify <file.csv>` | `--offline` | Classifica gli strumenti (Bucket A/B) e crea/aggiorna il sidecar `*.classify.json`
|
|
104
|
-
| `validate <file.csv>` | `--lang it\|en` | Exit 0 con avvisi; exit 1 in caso di errori bloccanti
|
|
105
|
-
| `rates --check` | — | Mostra la copertura della snapshot BCE in locale
|
|
106
|
-
| `rates --update` | — | Scarica i tassi aggiornati dall'API BCE
|
|
107
|
-
| `config --lang it\|en` | — | Salva la lingua preferita
|
|
108
|
-
| `config --show` | — | Mostra la lingua correntemente impostata
|
|
109
|
-
| `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documentato in fondo
|
|
116
|
+
| Comando | Flag principali | Note |
|
|
117
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Non aggiorna mai i tassi BCE da solo — esegui `rates --update` periodicamente; usa il sidecar di `classify` se presente |
|
|
119
|
+
| `classify <file.csv>` | `--offline` | Classifica gli strumenti (Bucket A/B) e crea/aggiorna il sidecar `*.classify.json` |
|
|
120
|
+
| `validate <file.csv>` | `--lang it\|en` | Exit 0 con avvisi; exit 1 in caso di errori bloccanti |
|
|
121
|
+
| `rates --check` | — | Mostra la copertura della snapshot BCE in locale |
|
|
122
|
+
| `rates --update` | — | Scarica i tassi aggiornati dall'API BCE |
|
|
123
|
+
| `config --lang it\|en` | — | Salva la lingua preferita |
|
|
124
|
+
| `config --show` | — | Mostra la lingua correntemente impostata |
|
|
125
|
+
| `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documentato in fondo |
|
|
110
126
|
|
|
111
127
|
Precedenza lingua: `--lang` > lingua salvata > italiano (default).
|
|
112
128
|
|
|
@@ -157,7 +173,7 @@ if (parser.warnings.length > 0) {
|
|
|
157
173
|
// 2. Classificazione (Bucket A/B) — facoltativa, abilita Quadro RT/RM nel report
|
|
158
174
|
const classification = await new Classifier().classify(
|
|
159
175
|
transactions,
|
|
160
|
-
"trades.classify.json", // sidecar persistente;
|
|
176
|
+
"trades.classify.json", // sidecar persistente; assente in modalità stateless
|
|
161
177
|
);
|
|
162
178
|
|
|
163
179
|
// 3. Calcolo
|
|
@@ -227,15 +243,15 @@ Configurazione tipica per un client MCP (es. Claude Desktop, `claude_desktop_con
|
|
|
227
243
|
|
|
228
244
|
Il server espone 3 tool:
|
|
229
245
|
|
|
230
|
-
| Tool | Descrizione
|
|
231
|
-
| ---------------------- |
|
|
232
|
-
| `parse_transactions` |
|
|
233
|
-
| `classify_instruments` | Classifica gli ISIN in Bucket A/B (modalità stateless — nessun sidecar)
|
|
234
|
-
| `calculate_gains` | Calcola plusvalenze/minusvalenze (LIFO/FIFO) e, se disponibile, Quadro RT/RM
|
|
246
|
+
| Tool | Descrizione |
|
|
247
|
+
| ---------------------- | -------------------------------------------------------------------------------------- |
|
|
248
|
+
| `parse_transactions` | Esegue il parsing di un CSV DEGIRO, restituendo `transactions`/`warnings`/`incomeRows` |
|
|
249
|
+
| `classify_instruments` | Classifica gli ISIN in Bucket A/B (modalità stateless — nessun sidecar) |
|
|
250
|
+
| `calculate_gains` | Calcola plusvalenze/minusvalenze (LIFO/FIFO) e, se disponibile, Quadro RT/RM |
|
|
235
251
|
|
|
236
252
|
`classify_instruments` supporta `existingClassification`, `overrides` e `offline: true` per
|
|
237
253
|
funzionare senza rete e senza accesso al filesystem — pensato per essere invocato ripetutamente
|
|
238
|
-
da un agente
|
|
254
|
+
da un agente in chiamate successive, mantenendo lo stato lato client.
|
|
239
255
|
|
|
240
256
|
### Domande frequenti
|
|
241
257
|
|
|
@@ -249,7 +265,7 @@ Le righe vengono saltate (senza bloccare il calcolo) quando: l'ISIN è vuoto, la
|
|
|
249
265
|
Il CSV contiene una vendita per un titolo senza un acquisto precedente nel file. L'acquisto potrebbe essere in un export di un anno precedente non incluso. Usa `validate` per esaminare le transazioni parsate.
|
|
250
266
|
|
|
251
267
|
**I tassi BCE sono scaduti**
|
|
252
|
-
Esegui `minus-tracker rates --update`. Il comando `calc`
|
|
268
|
+
Esegui `minus-tracker rates --update`. Il comando `calc` non aggiorna mai i tassi da solo: l'aggiornamento è sempre un'azione esplicita.
|
|
253
269
|
|
|
254
270
|
**LIFO o FIFO?**
|
|
255
271
|
Il Regime Dichiarativo utilizza il LIFO come metodo predefinito ai sensi della normativa fiscale italiana. Il FIFO è disponibile per confronto. Consulta il tuo commercialista per conferma.
|
|
@@ -393,16 +409,16 @@ npx @gabrielerandelli/minus-tracker calc trades.csv
|
|
|
393
409
|
|
|
394
410
|
### CLI Usage
|
|
395
411
|
|
|
396
|
-
| Command | Key flags | Notes
|
|
397
|
-
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
398
|
-
| `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` |
|
|
399
|
-
| `classify <file.csv>` | `--offline` | Classifies instruments (Bucket A/B) and creates/updates the `*.classify.json` sidecar
|
|
400
|
-
| `validate <file.csv>` | `--lang it\|en` | Exit 0 with warnings; exit 1 on hard errors
|
|
401
|
-
| `rates --check` | — | Shows bundled ECB snapshot coverage
|
|
402
|
-
| `rates --update` | — | Fetches fresh rates from the ECB API
|
|
403
|
-
| `config --lang it\|en` | — | Saves language preference
|
|
404
|
-
| `config --show` | — | Shows current language setting
|
|
405
|
-
| `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documented below
|
|
412
|
+
| Command | Key flags | Notes |
|
|
413
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
414
|
+
| `calc <file.csv>` | `--method LIFO\|FIFO` (default: LIFO), `--lang it\|en`, `--json`, `--export-dichiarazione [path]`, `--carry-forward` | Never fetches ECB rates on its own — run `rates --update` periodically; uses `classify`'s sidecar when present |
|
|
415
|
+
| `classify <file.csv>` | `--offline` | Classifies instruments (Bucket A/B) and creates/updates the `*.classify.json` sidecar |
|
|
416
|
+
| `validate <file.csv>` | `--lang it\|en` | Exit 0 with warnings; exit 1 on hard errors |
|
|
417
|
+
| `rates --check` | — | Shows bundled ECB snapshot coverage |
|
|
418
|
+
| `rates --update` | — | Fetches fresh rates from the ECB API |
|
|
419
|
+
| `config --lang it\|en` | — | Saves language preference |
|
|
420
|
+
| `config --show` | — | Shows current language setting |
|
|
421
|
+
| `stress-test` | `--range N-M`, `--keep`, `--json`, `--output-dir` | Documented below |
|
|
406
422
|
|
|
407
423
|
Language precedence: `--lang` flag > saved config > Italian (default).
|
|
408
424
|
|
|
@@ -543,7 +559,7 @@ Rows are skipped (without aborting the calculation) when: the ISIN is empty, the
|
|
|
543
559
|
The CSV contains a SELL for a position that has no prior BUY in the same file. The BUY may be in a prior year's export that was not included. Use `validate` to inspect the parsed transactions.
|
|
544
560
|
|
|
545
561
|
**ECB rates are outdated**
|
|
546
|
-
Run `minus-tracker rates --update`. The `calc` command
|
|
562
|
+
Run `minus-tracker rates --update`. The `calc` command never fetches rates on its own — refreshing is always an explicit, user-invoked action.
|
|
547
563
|
|
|
548
564
|
**LIFO or FIFO?**
|
|
549
565
|
LIFO is the standard lot-matching method under Italian tax law for the Regime Dichiarativo. FIFO is available for comparison or other jurisdictions. Consult your tax advisor for confirmation.
|
package/dist/cli/index.js
CHANGED
|
@@ -9,6 +9,9 @@ var it = {
|
|
|
9
9
|
errorInvalidCsv: "CSV non valido: impossibile analizzare il file",
|
|
10
10
|
errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,
|
|
11
11
|
errorNoOpenLots: (isin, date) => `Nessun lotto aperto per ISIN ${isin} in data ${date}`,
|
|
12
|
+
errorCannotReadFile: (path7) => `Impossibile leggere il file: ${path7}`,
|
|
13
|
+
errorCannotLoadSidecar: (path7) => `Impossibile caricare il sidecar: ${path7}`,
|
|
14
|
+
errorCannotWriteExport: (path7) => `Impossibile scrivere l'export della dichiarazione: ${path7}`,
|
|
12
15
|
warnMissingIsin: (row) => `Riga ${row}: ISIN mancante \u2014 riga ignorata`,
|
|
13
16
|
warnUnsupportedCurrency: (row, currency) => `Riga ${row}: valuta non supportata ${currency} \u2014 riga ignorata`,
|
|
14
17
|
warnNoEcbRate: (row, currency, date) => `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} \u2014 riga ignorata`,
|
|
@@ -88,7 +91,7 @@ var it = {
|
|
|
88
91
|
quadroRMRitenuta: "Ritenuta estera (credito)",
|
|
89
92
|
quadroRMCedole: "[RM-C] Cedole obbligazionarie",
|
|
90
93
|
dichiarazioneDisclaimer: "minus-tracker \xE8 un ausilio al calcolo, non consulenza fiscale.",
|
|
91
|
-
warnNoDichiarazioneSidecar: "
|
|
94
|
+
warnNoDichiarazioneSidecar: "Suggerimento: esegui prima 'minus-tracker classify <file>' per abilitare il Quadro RT/RM.",
|
|
92
95
|
warnNoCarryForwardProvided: "(eventuali perdite pregresse non applicate)"
|
|
93
96
|
};
|
|
94
97
|
|
|
@@ -98,6 +101,9 @@ var en = {
|
|
|
98
101
|
errorInvalidCsv: "Invalid CSV: unable to parse",
|
|
99
102
|
errorMissingColumn: (col) => `Missing required column: ${col}`,
|
|
100
103
|
errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,
|
|
104
|
+
errorCannotReadFile: (path7) => `Cannot read file: ${path7}`,
|
|
105
|
+
errorCannotLoadSidecar: (path7) => `Cannot load sidecar: ${path7}`,
|
|
106
|
+
errorCannotWriteExport: (path7) => `Cannot write dichiarazione export: ${path7}`,
|
|
101
107
|
warnMissingIsin: (row) => `Row ${row}: missing ISIN \u2014 skipped`,
|
|
102
108
|
warnUnsupportedCurrency: (row, currency) => `Row ${row}: unsupported currency ${currency} \u2014 skipped`,
|
|
103
109
|
warnNoEcbRate: (row, currency, date) => `Row ${row}: no ECB rate for ${currency} on ${date} \u2014 skipped`,
|
|
@@ -177,7 +183,7 @@ var en = {
|
|
|
177
183
|
quadroRMRitenuta: "Foreign withholding (credit)",
|
|
178
184
|
quadroRMCedole: "[RM-C] Bond coupons",
|
|
179
185
|
dichiarazioneDisclaimer: "minus-tracker \xE8 un ausilio al calcolo, non consulenza fiscale.",
|
|
180
|
-
warnNoDichiarazioneSidecar: "
|
|
186
|
+
warnNoDichiarazioneSidecar: "Tip: run 'minus-tracker classify <file>' first to enable Quadro RT/RM.",
|
|
181
187
|
warnNoCarryForwardProvided: "(prior-year losses not applied)"
|
|
182
188
|
};
|
|
183
189
|
|
|
@@ -1689,8 +1695,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
|
|
|
1689
1695
|
try {
|
|
1690
1696
|
csv = fs4.readFileSync(filePath, "utf8");
|
|
1691
1697
|
} catch {
|
|
1692
|
-
stderr.write(
|
|
1693
|
-
`);
|
|
1698
|
+
stderr.write(s.errorCannotReadFile(filePath) + "\n");
|
|
1694
1699
|
return 1;
|
|
1695
1700
|
}
|
|
1696
1701
|
const method = flags["method"] ?? "LIFO";
|
|
@@ -1705,8 +1710,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
|
|
|
1705
1710
|
const classifier = new Classifier({ interactive: false });
|
|
1706
1711
|
classification = await classifier.load(sidecarPath);
|
|
1707
1712
|
} catch {
|
|
1708
|
-
stderr.write(
|
|
1709
|
-
`);
|
|
1713
|
+
stderr.write(s.errorCannotLoadSidecar(sidecarPath) + "\n");
|
|
1710
1714
|
return 1;
|
|
1711
1715
|
}
|
|
1712
1716
|
}
|
|
@@ -1776,8 +1780,7 @@ async function runCalc(positional, flags, s, stdout, stderr) {
|
|
|
1776
1780
|
try {
|
|
1777
1781
|
await report.dichiarazione.exportTo(exportPath);
|
|
1778
1782
|
} catch {
|
|
1779
|
-
stderr.write(
|
|
1780
|
-
`);
|
|
1783
|
+
stderr.write(s.errorCannotWriteExport(exportPath) + "\n");
|
|
1781
1784
|
return 1;
|
|
1782
1785
|
}
|
|
1783
1786
|
}
|
|
@@ -1869,18 +1872,45 @@ function getSnapshotPath() {
|
|
|
1869
1872
|
}
|
|
1870
1873
|
return path4.join(configDir, "minus-tracker", "ecb-rates.json");
|
|
1871
1874
|
}
|
|
1875
|
+
function countMissingBusinessDays(startDate, endDate, dates) {
|
|
1876
|
+
let missing = 0;
|
|
1877
|
+
const cursor = /* @__PURE__ */ new Date(startDate + "T00:00:00Z");
|
|
1878
|
+
const end = /* @__PURE__ */ new Date(endDate + "T00:00:00Z");
|
|
1879
|
+
while (cursor <= end) {
|
|
1880
|
+
const day = cursor.getUTCDay();
|
|
1881
|
+
const iso = cursor.toISOString().slice(0, 10);
|
|
1882
|
+
if (day !== 0 && day !== 6 && dates[iso] === void 0) missing++;
|
|
1883
|
+
cursor.setUTCDate(cursor.getUTCDate() + 1);
|
|
1884
|
+
}
|
|
1885
|
+
return missing;
|
|
1886
|
+
}
|
|
1872
1887
|
function getCoverage(snapshot) {
|
|
1873
1888
|
let start = "9999-12-31";
|
|
1874
1889
|
let end = "0000-01-01";
|
|
1875
1890
|
const currencies = [];
|
|
1891
|
+
const gapEntries = [];
|
|
1876
1892
|
for (const [ccy, dates] of Object.entries(snapshot)) {
|
|
1877
1893
|
currencies.push(ccy);
|
|
1878
|
-
|
|
1894
|
+
const keys = Object.keys(dates).sort();
|
|
1895
|
+
for (const d of keys) {
|
|
1879
1896
|
if (d < start) start = d;
|
|
1880
1897
|
if (d > end) end = d;
|
|
1881
1898
|
}
|
|
1899
|
+
if (keys.length > 0) {
|
|
1900
|
+
const missing = countMissingBusinessDays(
|
|
1901
|
+
keys[0],
|
|
1902
|
+
keys[keys.length - 1],
|
|
1903
|
+
dates
|
|
1904
|
+
);
|
|
1905
|
+
if (missing > 0) gapEntries.push(`${ccy}: ${missing}`);
|
|
1906
|
+
}
|
|
1882
1907
|
}
|
|
1883
|
-
return {
|
|
1908
|
+
return {
|
|
1909
|
+
start,
|
|
1910
|
+
end,
|
|
1911
|
+
currencies: currencies.sort().join(", "),
|
|
1912
|
+
gaps: gapEntries.sort().join(", ")
|
|
1913
|
+
};
|
|
1884
1914
|
}
|
|
1885
1915
|
async function fetchEcbData(currency) {
|
|
1886
1916
|
return new Promise((resolve2, reject) => {
|
|
@@ -1944,9 +1974,9 @@ async function updateRates(snapshotPath, stdout, stderr, s) {
|
|
|
1944
1974
|
async function runRates(positional, flags, s, stdout, stderr) {
|
|
1945
1975
|
if (flags["check"]) {
|
|
1946
1976
|
const snapshot = getActiveSnapshot();
|
|
1947
|
-
const { start, end, currencies } = getCoverage(snapshot);
|
|
1977
|
+
const { start, end, currencies, gaps } = getCoverage(snapshot);
|
|
1948
1978
|
stdout.write(s.ratesCoverage(start, end, currencies) + "\n");
|
|
1949
|
-
stdout.write(s.ratesGapsNone + "\n");
|
|
1979
|
+
stdout.write((gaps ? s.ratesGaps(gaps) : s.ratesGapsNone) + "\n");
|
|
1950
1980
|
return 0;
|
|
1951
1981
|
}
|
|
1952
1982
|
if (flags["update"]) {
|
|
@@ -2512,7 +2542,9 @@ async function main() {
|
|
|
2512
2542
|
range: { type: "string" },
|
|
2513
2543
|
keep: { type: "boolean", default: false },
|
|
2514
2544
|
"output-dir": { type: "string" },
|
|
2515
|
-
offline: { type: "boolean", default: false }
|
|
2545
|
+
offline: { type: "boolean", default: false },
|
|
2546
|
+
"carry-forward": { type: "string", multiple: true },
|
|
2547
|
+
"export-dichiarazione": { type: "string" }
|
|
2516
2548
|
},
|
|
2517
2549
|
allowPositionals: true,
|
|
2518
2550
|
strict: false
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts","../../src/i18n/it.ts","../../src/i18n/en.ts","../../src/i18n/settings.ts","../../src/i18n/index.ts","../../src/errors.ts","../../src/cli/commands/calc.ts","../../src/rates/index.ts","../../src/parser/warnings.ts","../../src/parser/index.ts","../../src/dichiarazione/engine.ts","../../src/calculator/index.ts","../../src/classifier/index.ts","../../src/cli/renderer.ts","../../src/cli/commands/validate.ts","../../src/cli/commands/rates.ts","../../src/cli/commands/config.ts","../../src/cli/commands/stress-test.ts","../../src/stress/generator.ts","../../src/stress/runner.ts","../../src/stress/reporter.ts","../../src/cli/commands/classify.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolveLocale, getStrings } from \"../i18n/index.js\";\nimport { ParseError, CalculationError } from \"../errors.js\";\nimport { runCalc } from \"./commands/calc.js\";\nimport { runValidate } from \"./commands/validate.js\";\nimport { runRates } from \"./commands/rates.js\";\nimport { runConfig } from \"./commands/config.js\";\nimport { runStressTest } from \"./commands/stress-test.js\";\nimport { runClassify } from \"./commands/classify.js\";\nimport { ClassificationError } from \"../errors.js\";\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n lang: { type: \"string\" },\n method: { type: \"string\" },\n year: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n check: { type: \"boolean\", default: false },\n update: { type: \"boolean\", default: false },\n show: { type: \"boolean\", default: false },\n range: { type: \"string\" },\n keep: { type: \"boolean\", default: false },\n \"output-dir\": { type: \"string\" },\n offline: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // Resolve locale once — exits 2 on invalid value\n const locale = resolveLocale(values.lang as string | undefined);\n const s = getStrings(locale);\n\n const command = positionals[0];\n const restPositionals = positionals.slice(1);\n const flags = values as Record<string, string | boolean>;\n\n const stdout = process.stdout;\n const stderr = process.stderr;\n\n let exitCode = 0;\n\n try {\n switch (command) {\n case \"calc\":\n exitCode = await runCalc(restPositionals, flags, s, stdout, stderr);\n break;\n case \"validate\":\n exitCode = await runValidate(restPositionals, flags, s, stdout, stderr);\n break;\n case \"rates\":\n exitCode = await runRates(restPositionals, flags, s, stdout, stderr);\n break;\n case \"config\":\n exitCode = await runConfig(restPositionals, flags, s, stdout, stderr);\n break;\n case \"stress-test\":\n exitCode = await runStressTest(restPositionals, flags, stdout, stderr);\n break;\n case \"classify\":\n exitCode = await runClassify(restPositionals, flags, s, stdout, stderr);\n break;\n default:\n stderr.write(\n \"Usage: minus-tracker <calc|validate|rates|config|stress-test|classify> [options] [file]\\n\",\n );\n exitCode = 2;\n }\n } catch (err) {\n if (err instanceof ClassificationError) {\n stderr.write(err.message + \"\\n\");\n exitCode = 1;\n } else if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n exitCode = 1;\n } else if (err instanceof CalculationError) {\n stderr.write(s.errorNoOpenLots(err.isin, err.date) + \"\\n\");\n exitCode = 1;\n } else {\n throw err;\n }\n }\n\n process.exit(exitCode);\n}\n\nmain().catch((err) => {\n process.stderr.write(`Unhandled error: ${String(err)}\\n`);\n process.exit(1);\n});\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const it: LocaleStrings = {\n numberLocale: \"it-IT\",\n\n errorInvalidCsv: \"CSV non valido: impossibile analizzare il file\",\n errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,\n errorNoOpenLots: (isin, date) =>\n `Nessun lotto aperto per ISIN ${isin} in data ${date}`,\n\n warnMissingIsin: (row) => `Riga ${row}: ISIN mancante — riga ignorata`,\n warnUnsupportedCurrency: (row, currency) =>\n `Riga ${row}: valuta non supportata ${currency} — riga ignorata`,\n warnNoEcbRate: (row, currency, date) =>\n `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} — riga ignorata`,\n warnQuantityZero: (row) => `Riga ${row}: quantità pari a 0 — riga ignorata`,\n warnMissingIsinIncome: (row) =>\n `Riga ${row}: ISIN mancante su riga di reddito — riga ignorata`,\n warnOrphanWithholding: (isin, date) =>\n `Riga di ritenuta su ${isin}/${date} senza riga di reddito corrispondente — riga ignorata`,\n\n warnMultipleYears:\n \"Il CSV contiene transazioni di più anni — filtra per un singolo anno per un calcolo accurato.\",\n\n headerMethod: \"METODO\",\n headerTaxYear: \"ANNO FISCALE\",\n headerIsin: \"ISIN\",\n headerProduct: \"TITOLO\",\n headerQty: \"QTÀ\",\n headerBuyDate: \"DATA ACQUISTO\",\n headerSellDate: \"DATA VENDITA\",\n headerBuyEur: \"ACQUISTO EUR\",\n headerSellEur: \"VENDITA EUR\",\n headerGainLoss: \"GUADAGNO/PERDITA\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"RISULTATO NETTO\",\n summaryWarnings: \"AVVERTENZE\",\n summaryGenerated: \"Generato\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transazioni analizzate, ${errors} errori gravi`,\n validateWarn: (count, reason) =>\n `AVVISO: ${count} righe ignorate (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Copertura: ${start} → ${end} | Valute: ${currencies}`,\n ratesGapsNone: \"Lacune: nessuna\",\n ratesGaps: (list) => `Lacune: ${list}`,\n ratesUpdateFetching: \"Recupero dati BCE SDMX in corso...\",\n ratesUpdateDone: (n) => `Completato. Aggiunte ${n} nuove date.`,\n ratesSnapshotWritten: (path) => `Snapshot scritto in ${path}`,\n ratesAutoUpdateStart:\n \"Le rate BCE non sono aggiornate. Aggiornamento automatico in corso...\",\n ratesAutoUpdateFailed:\n \"Aggiornamento automatico fallito. Si procede con le rate disponibili.\",\n\n configLangSet: (lang) => `Lingua impostata su: ${lang}`,\n configCurrentLang: (lang) => `Lingua corrente: ${lang}`,\n\n classifyStarting: (n) => `Classificazione ISIN in corso... (${n} strumenti)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Rilevato: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Tipo di strumento:\",\n classifyDone: (confirmed, total) =>\n `Classificazione completata (${confirmed}/${total} confermati).`,\n classifyWritten: (path) => `Scritto: ${path}`,\n classifyMergePrompt: (existing) =>\n `File già esistente (${existing} ISIN confermati). Aggiungere i nuovi ISIN? [Y/n]: `,\n classifyOfflineWarning: \"Modalità offline: ricerca OpenFIGI saltata.\",\n classifyUnknownType: (type) =>\n `Tipo non riconosciuto: ${type}. Classificare manualmente.`,\n classifyNonTtyError:\n \"Modalità interattiva richiede un terminale (TTY). Usa --offline per classificare senza terminale.\",\n\n bucketAHeader: \"BUCKET A — REDDITI DA CAPITALE (non compensabili)\",\n bucketBHeader: \"BUCKET B — REDDITI DIVERSI\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"BTP/WL\",\n bucketATotalTax: \"TOTALE IMPOSTA\",\n bucketBCarryApplied: (year) => `RIPORTO ${year}`,\n bucketBResult: \"RISULTATO\",\n bucketBCarryNote: \"(riportabile ai prossimi 4 anni)\",\n warnMixedBuckets:\n \"Le perdite in Bucket B non compensano le plusvalenze in Bucket A.\",\n warnMixedAssets:\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} non trovato nella mappa di classificazione — assegnato a Bucket B.`,\n carryForwardInvalidFormat:\n \"Formato --carry-forward non valido. Usa: AAAA:importo (es. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) =>\n `── MODELLO REDDITI PF [ANNO D'IMPOSTA: ${anno}]`,\n dichiarazioneNota:\n \"Nota: i valori riportano i codici riga indicativi — verificare la modulistica ufficiale.\",\n dichiarazioneWarningRow:\n \"⚠ I codici riga tra parentesi quadre sono indicativi. Verificare la versione corrente del modello.\",\n quadroRTHeader: \"QUADRO RT — SEZIONE II (redditi diversi)\",\n quadroRTPlusvalenze: \"[RT-P] Plusvalenze totali\",\n quadroRTMinusvalenze: \"[RT-M] Minusvalenze totali\",\n quadroRTDifferenza: \"[RT-D] Differenza\",\n quadroRTRiporto: (anno) => `[RT-C*] Riporto ${anno} (consumato)`,\n quadroRTImponibile: \"[RT-N] Imponibile netto\",\n quadroRTImposta: \"[RT-I] Imposta (26%)\",\n quadroRTRiportabile: \"[RT-R] Minusvalenze da riportare\",\n quadroRTPerdita: \"perdita netta:\",\n quadroRMHeader: \"QUADRO RM (redditi da capitale)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR plusvalenze (26%)\",\n quadroRMImposta: \"Imposta sostitutiva\",\n quadroRMBtp: \"[RM-A2] BTP/WL plusvalenze (12,5%)\",\n quadroRMDividendi: \"[RM-D] Dividendi esteri lordi\",\n quadroRMRitenuta: \"Ritenuta estera (credito)\",\n quadroRMCedole: \"[RM-C] Cedole obbligazionarie\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar: \"Eseguire prima: minus-tracker classify <file>\",\n warnNoCarryForwardProvided: \"(eventuali perdite pregresse non applicate)\",\n};\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const en: LocaleStrings = {\n numberLocale: \"en-US\",\n\n errorInvalidCsv: \"Invalid CSV: unable to parse\",\n errorMissingColumn: (col) => `Missing required column: ${col}`,\n errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,\n\n warnMissingIsin: (row) => `Row ${row}: missing ISIN — skipped`,\n warnUnsupportedCurrency: (row, currency) =>\n `Row ${row}: unsupported currency ${currency} — skipped`,\n warnNoEcbRate: (row, currency, date) =>\n `Row ${row}: no ECB rate for ${currency} on ${date} — skipped`,\n warnQuantityZero: (row) => `Row ${row}: quantity is 0 — skipped`,\n warnMissingIsinIncome: (row) =>\n `Row ${row}: income row has blank ISIN — skipped`,\n warnOrphanWithholding: (isin, date) =>\n `Withholding row on ${isin}/${date} has no matching income row — skipped`,\n\n warnMultipleYears:\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n\n headerMethod: \"METHOD\",\n headerTaxYear: \"TAX YEAR\",\n headerIsin: \"ISIN\",\n headerProduct: \"PRODUCT\",\n headerQty: \"QTY\",\n headerBuyDate: \"BUY DATE\",\n headerSellDate: \"SELL DATE\",\n headerBuyEur: \"BUY EUR\",\n headerSellEur: \"SELL EUR\",\n headerGainLoss: \"GAIN/LOSS\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"NET RESULT\",\n summaryWarnings: \"WARNINGS\",\n summaryGenerated: \"Generated\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transactions parsed, ${errors} hard errors`,\n validateWarn: (count, reason) => `WARN: ${count} rows skipped (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Coverage: ${start} → ${end} | Currencies: ${currencies}`,\n ratesGapsNone: \"Gaps: none\",\n ratesGaps: (list) => `Gaps: ${list}`,\n ratesUpdateFetching: \"Fetching ECB SDMX...\",\n ratesUpdateDone: (n) => `Done. Added ${n} new dates.`,\n ratesSnapshotWritten: (path) => `Snapshot written to ${path}`,\n ratesAutoUpdateStart: \"ECB rates are stale. Auto-updating…\",\n ratesAutoUpdateFailed: \"Auto-update failed. Proceeding with available rates.\",\n\n configLangSet: (lang) => `Language set to: ${lang}`,\n configCurrentLang: (lang) => `Current language: ${lang}`,\n\n classifyStarting: (n) => `Classifying ISINs... (${n} instruments)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Detected: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Instrument type:\",\n classifyDone: (confirmed, total) =>\n `Classification complete (${confirmed}/${total} confirmed).`,\n classifyWritten: (path) => `Written: ${path}`,\n classifyMergePrompt: (existing) =>\n `File already exists (${existing} confirmed ISINs). Add new ISINs? [Y/n]: `,\n classifyOfflineWarning: \"Offline mode: OpenFIGI lookup skipped.\",\n classifyUnknownType: (type) =>\n `Unrecognized type: ${type}. Please classify manually.`,\n classifyNonTtyError:\n \"Interactive mode requires a TTY. Use --offline to classify without a terminal.\",\n\n bucketAHeader: \"BUCKET A — CAPITAL INCOME (non-offsettable)\",\n bucketBHeader: \"BUCKET B — MISCELLANEOUS INCOME\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"Govt Bond WL\",\n bucketATotalTax: \"TOTAL TAX\",\n bucketBCarryApplied: (year) => `CARRY ${year}`,\n bucketBResult: \"RESULT\",\n bucketBCarryNote: \"(carriable over the next 4 years)\",\n warnMixedBuckets: \"Bucket B losses do not offset Bucket A plusvalenze.\",\n warnMixedAssets:\n \"WARNING: CSV contains mixed instrument types (e.g. ETFs + Stocks).\\n\" +\n \" Single-bucket calculation may not be fiscally correct.\\n\" +\n \" Run: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n carryForwardInvalidFormat:\n \"Invalid --carry-forward format. Use: YYYY:amount (e.g. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) => `── MODELLO REDDITI PF [TAX YEAR: ${anno}]`,\n dichiarazioneNota:\n \"Note: row codes are indicative — verify against the official tax form.\",\n dichiarazioneWarningRow:\n \"⚠ Row codes in square brackets are indicative. Check the current form version.\",\n quadroRTHeader: \"QUADRO RT — SECTION II (capital gains)\",\n quadroRTPlusvalenze: \"[RT-P] Total gains\",\n quadroRTMinusvalenze: \"[RT-M] Total losses\",\n quadroRTDifferenza: \"[RT-D] Difference\",\n quadroRTRiporto: (anno) => `[RT-C*] Prior loss ${anno} (applied)`,\n quadroRTImponibile: \"[RT-N] Net taxable\",\n quadroRTImposta: \"[RT-I] Tax (26%)\",\n quadroRTRiportabile: \"[RT-R] Losses to carry forward\",\n quadroRTPerdita: \"net loss:\",\n quadroRMHeader: \"QUADRO RM (capital income)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR gains (26%)\",\n quadroRMImposta: \"Substitute tax\",\n quadroRMBtp: \"[RM-A2] BTP/WL gains (12.5%)\",\n quadroRMDividendi: \"[RM-D] Foreign dividends (gross)\",\n quadroRMRitenuta: \"Foreign withholding (credit)\",\n quadroRMCedole: \"[RM-C] Bond coupons\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar: \"Run first: minus-tracker classify <file>\",\n warnNoCarryForwardProvided: \"(prior-year losses not applied)\",\n};\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { MinusTrackerConfig, SupportedLocale } from \"./types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport function getConfigPath(): string {\n if (process.platform === \"win32\") {\n const appData =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n return path.join(appData, \"minus-tracker\", \"config.json\");\n }\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n return path.join(xdg, \"minus-tracker\", \"config.json\");\n}\n\nexport function readConfig(): MinusTrackerConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), \"utf8\");\n return JSON.parse(raw) as MinusTrackerConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveLocale(lang: SupportedLocale): void {\n const configPath = getConfigPath();\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const existing = readConfig();\n const updated: MinusTrackerConfig = { ...existing, locale: lang };\n fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveLocale(cliLang?: string): SupportedLocale {\n // 1. --lang flag\n if (cliLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(cliLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${cliLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return cliLang as SupportedLocale;\n }\n // 2. env var\n const envLang = process.env.MINUS_TRACKER_LANG;\n if (envLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(envLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${envLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return envLang as SupportedLocale;\n }\n // 3. config file\n const config = readConfig();\n if (config.locale && SUPPORTED_LOCALES.includes(config.locale)) {\n return config.locale;\n }\n // 4. default\n return \"it\";\n}\n","import { it } from \"./it.js\";\nimport { en } from \"./en.js\";\nimport type { LocaleStrings, SupportedLocale } from \"./types.js\";\n\nexport { it, en };\nexport type { LocaleStrings, SupportedLocale };\nexport type { MinusTrackerConfig } from \"./types.js\";\nexport {\n resolveLocale,\n saveLocale,\n readConfig,\n getConfigPath,\n} from \"./settings.js\";\n\nexport function getStrings(locale: SupportedLocale): LocaleStrings {\n return locale === \"it\" ? it : en;\n}\n","export class ParseError extends Error {\n readonly code: \"INVALID_CSV\" | \"MISSING_COLUMN\";\n readonly columnName?: string;\n\n constructor(code: \"INVALID_CSV\");\n constructor(code: \"MISSING_COLUMN\", columnName: string);\n constructor(code: ParseError[\"code\"], columnName?: string) {\n const msg =\n code === \"INVALID_CSV\"\n ? \"Invalid CSV: unable to parse\"\n : `Missing required column: ${columnName}`;\n super(msg);\n this.name = \"ParseError\";\n this.code = code;\n this.columnName = columnName;\n }\n}\n\nexport class CalculationError extends Error {\n readonly isin: string;\n readonly date: string;\n\n constructor(isin: string, date: string) {\n super(`No open lots for ISIN ${isin} on ${date}`);\n this.name = \"CalculationError\";\n this.isin = isin;\n this.date = date;\n }\n}\n\nexport class ClassificationError extends Error {\n readonly code:\n | \"NETWORK_ERROR\"\n | \"SIDECAR_NOT_FOUND\"\n | \"SIDECAR_VERSION\"\n | \"SIDECAR_MALFORMED\"\n | \"WRITE_ERROR\";\n\n constructor(code: ClassificationError[\"code\"], message?: string) {\n super(\n message ??\n (code === \"SIDECAR_NOT_FOUND\"\n ? \"Sidecar file not found\"\n : code === \"SIDECAR_VERSION\"\n ? \"Sidecar version mismatch\"\n : code === \"SIDECAR_MALFORMED\"\n ? \"Sidecar file is malformed JSON\"\n : code === \"NETWORK_ERROR\"\n ? \"Network error contacting OpenFIGI\"\n : \"Failed to write sidecar file\"),\n );\n this.name = \"ClassificationError\";\n this.code = code;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Calculator } from \"../../calculator/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport { renderReport } from \"../renderer.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type {\n LotMethod,\n ClassificationMap,\n CarryForward,\n} from \"../../types.js\";\n\nexport async function runCalc(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\n \"Usage: minus-tracker calc [--method LIFO|FIFO] [--json] <file.csv>\\n\",\n );\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const method = (flags[\"method\"] as LotMethod) ?? \"LIFO\";\n if (method !== \"LIFO\" && method !== \"FIFO\") {\n stderr.write(\"--method must be LIFO or FIFO\\n\");\n return 2;\n }\n\n // Sidecar auto-discovery\n const sidecarPath = filePath.replace(/\\.csv$/i, \"\") + \".classify.json\";\n let classification: ClassificationMap | undefined;\n if (fs.existsSync(sidecarPath)) {\n try {\n const classifier = new Classifier({ interactive: false });\n classification = await classifier.load(sidecarPath);\n } catch {\n stderr.write(`Cannot load sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n }\n\n // --export-dichiarazione flag parsing (optional path value)\n const exportFlagRaw = flags[\"export-dichiarazione\"];\n const exportRequested =\n exportFlagRaw !== undefined && exportFlagRaw !== false;\n const exportPath =\n typeof exportFlagRaw === \"string\"\n ? exportFlagRaw\n : filePath.replace(/\\.csv$/i, \"\") + \".dichiarazione.json\";\n\n if (exportRequested && classification === undefined) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n return 1;\n }\n\n // --carry-forward flag parsing\n const rawCf: unknown = flags[\"carry-forward\"];\n const rawCarryForwards: string[] = Array.isArray(rawCf)\n ? (rawCf as string[])\n : rawCf\n ? [rawCf as string]\n : [];\n\n const cfRegex = /^\\d{4}:\\d+(\\.\\d+)?$/;\n for (const cf of rawCarryForwards) {\n if (!cfRegex.test(cf)) {\n stderr.write(s.carryForwardInvalidFormat + \"\\n\");\n return 2;\n }\n }\n\n // Load carry-forward config file\n const xdgConfig =\n process.env[\"XDG_CONFIG_HOME\"] ?? path.join(os.homedir(), \".config\");\n const cfConfigPath = path.join(\n xdgConfig,\n \"minus-tracker\",\n \"carryforward.json\",\n );\n const cfFromFile: Record<number, number> = {};\n if (fs.existsSync(cfConfigPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(cfConfigPath, \"utf-8\")) as {\n losses?: Array<{ year: number; amount: number }>;\n };\n for (const entry of raw.losses ?? []) {\n cfFromFile[entry.year] = entry.amount;\n }\n } catch {\n /* silently skip malformed config */\n }\n }\n\n // Merge: flag wins over config file\n const cfMerged: Record<number, number> = { ...cfFromFile };\n for (const cf of rawCarryForwards) {\n const [yearStr, amountStr] = cf.split(\":\");\n cfMerged[parseInt(yearStr, 10)] = parseFloat(amountStr);\n }\n const carryForward: CarryForward[] = Object.entries(cfMerged).map(\n ([year, amount]) => ({ year: parseInt(year, 10), amount }),\n );\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n const calculator = new Calculator(transactions, parser.warnings, {\n classification,\n carryForward: carryForward.length > 0 ? carryForward : undefined,\n incomeRows: parser.incomeRows,\n });\n const report = calculator.calculateGains(method);\n const carryForwardWasProvided = carryForward.length > 0;\n\n if (exportRequested) {\n try {\n await report.dichiarazione!.exportTo(exportPath);\n } catch {\n stderr.write(`Cannot write dichiarazione export: ${exportPath}\\n`);\n return 1;\n }\n }\n\n if (flags[\"json\"]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { dichiarazione: _dichiarazione, ...jsonReport } = report;\n stdout.write(JSON.stringify(jsonReport, null, 2) + \"\\n\");\n } else {\n stdout.write(renderReport(report, s, carryForwardWasProvided) + \"\\n\");\n }\n\n if (exportRequested) {\n stdout.write(s.classifyWritten(exportPath) + \"\\n\");\n } else if (!report.dichiarazione) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n }\n\n return 0;\n}\n","import { fileURLToPath } from \"node:url\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs\";\n\nexport type RatesSnapshot = Record<string, Record<string, number>>;\n\n// Load the bundled snapshot lazily; returns null if the file is absent (e.g. broken install)\nlet _bundled: RatesSnapshot | null | undefined = undefined;\n\nfunction getBundledSnapshot(): RatesSnapshot | null {\n if (_bundled !== undefined) return _bundled;\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const bundledPath = path.join(__dirname, \"../data/ecb-rates.json\");\n try {\n _bundled = JSON.parse(\n fs.readFileSync(bundledPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n _bundled = null;\n }\n return _bundled;\n}\n\nfunction getUserSnapshot(): RatesSnapshot | null {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ??\n path.join(process.env.USERPROFILE ?? \"~\", \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ??\n path.join(process.env.HOME ?? \"~\", \".config\");\n }\n const userPath = path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n try {\n return JSON.parse(fs.readFileSync(userPath, \"utf8\")) as RatesSnapshot;\n } catch {\n return null;\n }\n}\n\nexport function getActiveSnapshot(): RatesSnapshot {\n const bundled = getBundledSnapshot();\n const user = getUserSnapshot();\n if (!bundled && !user) {\n throw new Error(\n \"No ECB rates snapshot available. Run `minus-tracker rates --update` to fetch rates.\",\n );\n }\n if (!bundled) return user!;\n if (!user) return bundled;\n // Merge: user entries override bundled for same date keys\n const merged: RatesSnapshot = {};\n for (const ccy of new Set([...Object.keys(bundled), ...Object.keys(user)])) {\n merged[ccy] = { ...(bundled[ccy] ?? {}), ...(user[ccy] ?? {}) };\n }\n return merged;\n}\n\nexport function isSnapshotStale(\n snapshot: RatesSnapshot,\n today?: string,\n): boolean {\n const todayStr = today ?? new Date().toISOString().slice(0, 10);\n let maxDate = \"0000-01-01\";\n for (const dates of Object.values(snapshot)) {\n for (const d of Object.keys(dates)) {\n if (d > maxDate) maxDate = d;\n }\n }\n if (maxDate === \"0000-01-01\") return true;\n const diffDays =\n (new Date(todayStr).getTime() - new Date(maxDate).getTime()) /\n (1000 * 60 * 60 * 24);\n return diffDays >= 7;\n}\n\nfunction subtractDays(isoDate: string, days: number): string {\n const d = new Date(isoDate + \"T00:00:00Z\");\n d.setUTCDate(d.getUTCDate() - days);\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Look up ECB rate for a currency on a given date.\n * Returns 1.0 for EUR. Walks back up to 3 calendar days for weekend/holiday gaps.\n * Returns null if not found within the window.\n *\n * @param currency ISO 4217 currency code\n * @param date YYYY-MM-DD trade date\n * @param snapshot Optional override (used in tests to inject stub data)\n */\nexport function lookupRate(\n currency: string,\n date: string,\n snapshot?: RatesSnapshot,\n): number | null {\n if (currency === \"EUR\") return 1.0;\n\n const s = snapshot ?? getActiveSnapshot();\n const currencyRates = s[currency];\n if (!currencyRates) return null; // unsupported currency\n\n for (let i = 0; i <= 3; i++) {\n const d = i === 0 ? date : subtractDays(date, i);\n if (currencyRates[d] !== undefined) {\n return currencyRates[d];\n }\n }\n return null;\n}\n","export type WarningEntry =\n | { code: \"MISSING_ISIN\"; row: number }\n | { code: \"UNSUPPORTED_CURRENCY\"; row: number; currency: string }\n | { code: \"NO_ECB_RATE\"; row: number; currency: string; date: string }\n | { code: \"QUANTITY_ZERO\"; row: number }\n | { code: \"MISSING_ISIN_INCOME\"; row: number }\n | { code: \"ORPHAN_WITHHOLDING\"; isin: string; date: string };\n\nexport function warningToEnglish(w: WarningEntry): string {\n switch (w.code) {\n case \"MISSING_ISIN\":\n return `Row ${w.row}: missing ISIN — skipped`;\n case \"UNSUPPORTED_CURRENCY\":\n return `Row ${w.row}: unsupported currency ${w.currency} — skipped`;\n case \"NO_ECB_RATE\":\n return `Row ${w.row}: no ECB rate for ${w.currency} on ${w.date} — skipped`;\n case \"QUANTITY_ZERO\":\n return `Row ${w.row}: quantity is 0 — skipped`;\n case \"MISSING_ISIN_INCOME\":\n return `Row ${w.row}: blank ISIN on income row — skipped`;\n case \"ORPHAN_WITHHOLDING\":\n return `Withholding row for ${w.isin} on ${w.date}: no matching income row — skipped`;\n }\n}\n","import { Transaction, IncomeRow } from \"../types.js\";\nimport { ParseError } from \"../errors.js\";\nimport {\n lookupRate,\n getActiveSnapshot,\n RatesSnapshot,\n} from \"../rates/index.js\";\nimport { WarningEntry, warningToEnglish } from \"./warnings.js\";\n\nconst REQUIRED_COLUMNS = [\n \"ISIN\",\n \"Quantity\",\n \"Price\",\n \"Date\",\n \"Local value\",\n \"Local value currency\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Product\",\n] as const;\n\n// Income-row product keyword matching (case-insensitive substring match).\n// Tax keywords are checked first because \"DIVIDEND TAX\" also contains \"DIVIDEND\".\nconst INCOME_KEYWORDS = [\"DIVIDEND\", \"COUPON\", \"INTEREST\", \"CEDOLA\"] as const;\nconst TAX_KEYWORDS = [\"DIVIDEND TAX\", \"WITHHOLDING\", \"RITENUTA\"] as const;\n\ninterface IncomeCandidate {\n isin: string;\n product: string;\n date: string;\n incomeType: \"dividend\" | \"coupon\";\n localValue: number;\n currency: string;\n row: number;\n}\n\ninterface WithholdingCandidate {\n isin: string;\n date: string;\n amount: number; // absolute value, in source currency\n row: number;\n}\n\n/**\n * Parse a single CSV line, respecting RFC-4180-style double-quote escaping.\n * Does not support embedded newlines in fields (not needed for DEGIRO exports).\n */\nfunction parseCSVRow(line: string): string[] {\n const fields: string[] = [];\n let i = 0;\n\n while (i <= line.length) {\n // End of line — stop (handles trailing comma by not emitting extra empty field)\n if (i === line.length) break;\n\n if (line[i] === '\"') {\n // Quoted field\n let field = \"\";\n i++; // skip opening quote\n while (i < line.length) {\n if (line[i] === '\"' && i + 1 < line.length && line[i + 1] === '\"') {\n // Escaped double-quote\n field += '\"';\n i += 2;\n } else if (line[i] === '\"') {\n i++; // skip closing quote\n break;\n } else {\n field += line[i++];\n }\n }\n fields.push(field);\n if (i < line.length && line[i] === \",\") i++; // skip delimiter\n } else {\n // Unquoted field\n const start = i;\n while (i < line.length && line[i] !== \",\") i++;\n fields.push(line.slice(start, i));\n if (i < line.length) i++; // skip delimiter\n }\n }\n\n return fields;\n}\n\n/**\n * Parse a full CSV string (CRLF or LF line endings) into a 2-D array of strings.\n */\nfunction parseCSV(input: string): string[][] {\n const normalized = input.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n return normalized.split(\"\\n\").map(parseCSVRow);\n}\n\n/**\n * Convert a DEGIRO date \"DD-MM-YYYY\" to ISO format \"YYYY-MM-DD\".\n */\nfunction parseDate(ddmmyyyy: string): string {\n const [dd, mm, yyyy] = ddmmyyyy.split(\"-\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Parses a DEGIRO Transactions CSV export into normalised Transaction objects.\n *\n * Export from Activity → Transactions in the DEGIRO UI (not the Account Statement).\n * Supported currencies: EUR, USD, GBP, CHF. ECB historical rates are used for conversion.\n */\nexport class DEGIROParser {\n private _warningEntries: WarningEntry[] = [];\n private _snapshot?: RatesSnapshot;\n private _incomeRows: IncomeRow[] = [];\n\n constructor(snapshot?: RatesSnapshot) {\n this._snapshot = snapshot;\n }\n\n /**\n * Parse a DEGIRO Transactions CSV export string.\n *\n * @param csv - Raw UTF-8 string from the DEGIRO Transactions export.\n * @returns Array of normalised Transaction objects. Empty if no data rows are found.\n * @throws {ParseError} code `\"INVALID_CSV\"` — malformed CSV or binary content.\n * @throws {ParseError} code `\"MISSING_COLUMN\"` (+ `columnName`) — required column absent.\n *\n * Rows with missing ISIN, unsupported currency, zero quantity, or no ECB rate within\n * 3 trading days are skipped silently. Inspect `parser.warnings` for details.\n */\n parse(csv: string): Transaction[] {\n this._warningEntries = [];\n this._incomeRows = [];\n\n // Binary content (null bytes) is not valid CSV\n if (typeof csv !== \"string\" || csv.includes(\"\\x00\")) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n let rows: string[][];\n try {\n rows = parseCSV(csv);\n } catch {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n if (rows.length === 0) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n // Build column-name → index map from the header row\n const header = rows[0];\n const colIndex: Record<string, number> = {};\n for (let i = 0; i < header.length; i++) {\n colIndex[header[i].trim()] = i;\n }\n\n // Validate that every required column is present\n for (const col of REQUIRED_COLUMNS) {\n if (colIndex[col] === undefined) {\n throw new ParseError(\"MISSING_COLUMN\", col);\n }\n }\n\n // Resolve the rates snapshot once for the whole parse run\n const snapshot: RatesSnapshot = this._snapshot ?? getActiveSnapshot();\n\n const transactions: Transaction[] = [];\n const incomeCandidates: IncomeCandidate[] = [];\n const withholdingCandidates: WithholdingCandidate[] = [];\n\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r];\n // 1-indexed row number: header = 1, first data row = 2\n const rowIndex = r + 1;\n\n // Skip completely empty rows (including the trailing empty line after a\n // final newline, and rows that are all whitespace)\n if (row.every((cell) => cell.trim() === \"\")) continue;\n\n const get = (col: string): string => (row[colIndex[col]] ?? \"\").trim();\n\n const isin = get(\"ISIN\");\n const product = get(\"Product\");\n const qtyStr = get(\"Quantity\");\n const rawQty = parseFloat(qtyStr);\n const qtyIsZeroOrBlank = qtyStr === \"\" || isNaN(rawQty) || rawQty === 0;\n\n // --- Income-row detection (Quantity == 0 or blank) ---\n if (qtyIsZeroOrBlank) {\n const upperProduct = product.toUpperCase();\n const isTaxKeyword = TAX_KEYWORDS.some((k) => upperProduct.includes(k));\n const isIncomeKeyword =\n !isTaxKeyword &&\n INCOME_KEYWORDS.some((k) => upperProduct.includes(k));\n\n if (isTaxKeyword || isIncomeKeyword) {\n const rawLocalValue = parseFloat(get(\"Local value\"));\n const localValue = isNaN(rawLocalValue) ? 0 : rawLocalValue;\n const isoDate = parseDate(get(\"Date\"));\n\n if (isTaxKeyword && localValue < 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n withholdingCandidates.push({\n isin,\n date: isoDate,\n amount: Math.abs(localValue),\n row: rowIndex,\n });\n continue;\n }\n\n if (isIncomeKeyword && localValue > 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n const incomeType: \"dividend\" | \"coupon\" = upperProduct.includes(\n \"DIVIDEND\",\n )\n ? \"dividend\"\n : \"coupon\";\n incomeCandidates.push({\n isin,\n product,\n date: isoDate,\n incomeType,\n localValue,\n currency: get(\"Local value currency\"),\n row: rowIndex,\n });\n continue;\n }\n // Keyword matched but sign doesn't match the expected direction —\n // fall through to standard transaction handling below.\n }\n }\n\n // --- ISIN ---\n if (!isin) {\n this._warningEntries.push({ code: \"MISSING_ISIN\", row: rowIndex });\n continue;\n }\n\n // --- Quantity ---\n if (isNaN(rawQty) || rawQty === 0) {\n this._warningEntries.push({ code: \"QUANTITY_ZERO\", row: rowIndex });\n continue;\n }\n\n const type: \"BUY\" | \"SELL\" = rawQty > 0 ? \"BUY\" : \"SELL\";\n const quantity = Math.abs(rawQty);\n\n // --- Date ---\n const isoDate = parseDate(get(\"Date\"));\n\n // --- Currency & FX ---\n const currency = get(\"Local value currency\");\n const rawTotalLocal = parseFloat(get(\"Local value\"));\n const totalLocal = isNaN(rawTotalLocal) ? 0 : rawTotalLocal;\n\n let totalEUR: number;\n let fxRate: number | undefined;\n\n if (currency === \"EUR\") {\n totalEUR = Math.abs(totalLocal);\n fxRate = undefined;\n } else {\n const rate = lookupRate(currency, isoDate, snapshot);\n if (rate === null) {\n if (snapshot[currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: rowIndex,\n currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: rowIndex,\n currency,\n date: isoDate,\n });\n }\n continue;\n }\n totalEUR = Math.abs(totalLocal) / rate;\n fxRate = rate;\n }\n\n // --- Fees ---\n const rawFees = parseFloat(get(\"Transaction costs\"));\n const feesEUR = Math.abs(isNaN(rawFees) ? 0 : rawFees);\n\n // --- Remaining fields ---\n const pricePerUnit = parseFloat(get(\"Price\"));\n\n transactions.push({\n isin,\n product,\n date: isoDate,\n type,\n quantity,\n pricePerUnit,\n currency,\n totalLocal,\n totalEUR,\n feesEUR,\n fxRate,\n });\n }\n\n // --- Pair withholding candidates with income candidates by (ISIN, date) ---\n // Order-independent: candidates were collected regardless of CSV row order.\n const withholdingByKey = new Map<\n string,\n { isin: string; date: string; amount: number }\n >();\n for (const w of withholdingCandidates) {\n const key = `${w.isin}|${w.date}`;\n const existing = withholdingByKey.get(key);\n withholdingByKey.set(key, {\n isin: w.isin,\n date: w.date,\n amount: (existing?.amount ?? 0) + w.amount,\n });\n }\n const consumedKeys = new Set<string>();\n\n const incomeRows: IncomeRow[] = [];\n for (const c of incomeCandidates) {\n const key = `${c.isin}|${c.date}`;\n const matchedWithholding = withholdingByKey.get(key);\n\n let grossAmount: number;\n let withholdingTax: number;\n let fxRate: number | undefined;\n\n if (c.currency === \"EUR\") {\n grossAmount = Math.abs(c.localValue);\n withholdingTax = matchedWithholding?.amount ?? 0;\n fxRate = undefined;\n } else {\n const rate = lookupRate(c.currency, c.date, snapshot);\n if (rate === null) {\n if (snapshot[c.currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: c.row,\n currency: c.currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: c.row,\n currency: c.currency,\n date: c.date,\n });\n }\n continue;\n }\n grossAmount = Math.abs(c.localValue) / rate;\n withholdingTax = matchedWithholding\n ? matchedWithholding.amount / rate\n : 0;\n fxRate = rate;\n }\n\n if (matchedWithholding) consumedKeys.add(key);\n\n incomeRows.push({\n isin: c.isin,\n product: c.product,\n date: c.date,\n incomeType: c.incomeType,\n grossAmount,\n withholdingTax,\n currency: c.currency,\n fxRate,\n });\n }\n\n // Orphan withholding candidates — no matching income row for (ISIN, date)\n for (const [key, w] of withholdingByKey) {\n if (!consumedKeys.has(key)) {\n this._warningEntries.push({\n code: \"ORPHAN_WITHHOLDING\",\n isin: w.isin,\n date: w.date,\n });\n }\n }\n\n this._incomeRows = incomeRows;\n\n return transactions;\n }\n\n get warnings(): string[] {\n return this._warningEntries.map(warningToEnglish);\n }\n\n get warningEntries(): WarningEntry[] {\n return this._warningEntries;\n }\n\n get incomeRows(): IncomeRow[] {\n return this._incomeRows;\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport type {\n BucketAReport,\n BucketBReport,\n CarryForward,\n CarryForwardEntry,\n DichiarazioneReport,\n IncomeRow,\n QuadroRMReport,\n QuadroRTReport,\n} from \"../types.js\";\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nexport function buildQuadroRT(\n bucketB: BucketBReport,\n carryForward: CarryForward[],\n taxYear: number,\n): QuadroRTReport {\n const plusvalenze = bucketB.plusvalenze;\n const minusvalenze = bucketB.minusvalenze;\n const differenza = roundHalfUp(plusvalenze - minusvalenze);\n\n if (differenza > 0) {\n const sorted = [...carryForward].sort((a, b) => a.year - b.year);\n let remaining = differenza;\n const carryForwardApplied: CarryForwardEntry[] = [];\n\n for (const entry of sorted) {\n if (taxYear - entry.year > 4) continue;\n const consumed = Math.min(entry.amount, remaining);\n if (consumed > 0) {\n carryForwardApplied.push({\n annoOrigine: entry.year,\n importo: roundHalfUp(consumed),\n });\n remaining -= consumed;\n }\n }\n\n const imponibileNetto = roundHalfUp(remaining);\n const imposta = roundHalfUp(imponibileNetto * 0.26);\n\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied,\n imponibileNetto,\n imposta,\n carryForwardRiportato: [],\n };\n } else if (differenza < 0) {\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [\n { annoOrigine: taxYear, importo: roundHalfUp(Math.abs(differenza)) },\n ],\n };\n } else {\n return {\n plusvalenze,\n minusvalenze,\n differenza: 0,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [],\n };\n }\n}\n\nexport function buildQuadroRM(\n bucketA: BucketAReport | undefined,\n incomeRows: IncomeRow[],\n taxYear: number,\n): QuadroRMReport {\n const groups = bucketA?.groups ?? [];\n\n const group26 = groups.find((g) => g.taxRate === 0.26);\n const group125 = groups.find((g) => g.taxRate === 0.125);\n\n const capitaleAliquota26 = {\n plusvalenze: group26?.plusvalenze ?? 0,\n imposta: group26?.imposta ?? 0,\n };\n const capitaleAliquota125 = {\n plusvalenze: group125?.plusvalenze ?? 0,\n imposta: group125?.imposta ?? 0,\n };\n\n const dividendiEsteri = incomeRows\n .filter((r) => r.incomeType === \"dividend\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n lordo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n const cedole = incomeRows\n .filter((r) => r.incomeType === \"coupon\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n importo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n return {\n capitaleAliquota26,\n capitaleAliquota125,\n dividendiEsteri,\n cedole,\n };\n}\n\nexport function buildDichiarazioneReport(\n quadroRT: QuadroRTReport,\n quadroRM: QuadroRMReport,\n taxYear: number,\n): DichiarazioneReport {\n const version = 1;\n const modello = \"Redditi PF\" as const;\n const generatedAt = new Date().toISOString();\n\n return {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n exportTo: async (path: string) => {\n await writeFile(\n path,\n JSON.stringify(\n {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n },\n null,\n 2,\n ),\n );\n },\n };\n}\n","import type {\n Transaction,\n MatchedLot,\n GainsReport,\n LotMethod,\n CalculatorOptions,\n AssetClass,\n BucketAReport,\n BucketBReport,\n} from \"../types.js\";\nimport { CalculationError } from \"../errors.js\";\nimport {\n buildQuadroRT,\n buildQuadroRM,\n buildDichiarazioneReport,\n} from \"../dichiarazione/engine.js\";\n\ninterface Lot {\n date: string;\n quantity: number;\n pricePerUnitEUR: number;\n feesEUR: number;\n originalQty: number;\n fxRate?: number;\n}\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nfunction inferTaxYear(transactions: Transaction[]): {\n year: number;\n multipleYears: boolean;\n} {\n const counts: Record<string, number> = {};\n for (const t of transactions) {\n const y = t.date.slice(0, 4);\n counts[y] = (counts[y] ?? 0) + 1;\n }\n const years = Object.keys(counts);\n if (years.length === 0)\n return { year: new Date().getFullYear(), multipleYears: false };\n const year = parseInt(\n years.reduce((a, b) => (counts[a] >= counts[b] ? a : b)),\n );\n return { year, multipleYears: years.length > 1 };\n}\n\n/**\n * Computes Italian capital gains and losses from a list of parsed transactions.\n *\n * Uses LIFO or FIFO lot matching and converts all amounts to EUR via ECB rates.\n */\nexport class Calculator {\n private readonly _transactions: Transaction[];\n private readonly _parseWarnings: string[];\n private readonly _options: CalculatorOptions;\n\n constructor(\n transactions: Transaction[],\n parseWarnings?: string[],\n options?: CalculatorOptions,\n ) {\n this._transactions = transactions;\n this._parseWarnings = parseWarnings ?? [];\n this._options = options ?? {};\n }\n\n /**\n * Run LIFO or FIFO lot-matching over the transaction list.\n *\n * @param method - `\"LIFO\"` or `\"FIFO\"`.\n * @returns GainsReport with `plusvalenze`, `minusvalenze`, `netResult`, per-lot breakdown,\n * ECB rates used, and any accumulated warnings.\n * @throws {CalculationError} when a SELL has no matching open buy lots.\n * `error.isin` and `error.date` identify the problematic transaction.\n */\n calculateGains(method: LotMethod): GainsReport {\n const warnings: string[] = [...this._parseWarnings];\n\n // 1. Sort ascending by date; BUY before SELL on same date\n const sorted = [...this._transactions].sort((a, b) => {\n if (a.date < b.date) return -1;\n if (a.date > b.date) return 1;\n // Same date: BUY before SELL\n if (a.type === \"BUY\" && b.type === \"SELL\") return -1;\n if (a.type === \"SELL\" && b.type === \"BUY\") return 1;\n return 0;\n });\n\n // 2. Tax year inference\n const { year: taxYear, multipleYears } = inferTaxYear(sorted);\n if (multipleYears) {\n warnings.push(\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n );\n }\n\n // 3. Lot matching\n const openLots = new Map<string, Lot[]>();\n const matchedLots: MatchedLot[] = [];\n const ratesUsed: Record<string, number> = {};\n\n for (const tx of sorted) {\n // Collect rates used\n if (tx.fxRate !== undefined) {\n ratesUsed[`${tx.currency}:${tx.date}`] = tx.fxRate;\n }\n\n if (tx.type === \"BUY\") {\n const lot: Lot = {\n date: tx.date,\n quantity: tx.quantity,\n pricePerUnitEUR: tx.totalEUR / tx.quantity,\n feesEUR: tx.feesEUR,\n originalQty: tx.quantity,\n fxRate: tx.fxRate,\n };\n if (!openLots.has(tx.isin)) openLots.set(tx.isin, []);\n openLots.get(tx.isin)!.push(lot);\n } else {\n // SELL\n const lots = openLots.get(tx.isin);\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const sellPricePerUnitEUR = tx.totalEUR / tx.quantity;\n let remainingSellQty = tx.quantity;\n\n while (remainingSellQty > 0) {\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const lot = method === \"LIFO\" ? lots[lots.length - 1] : lots[0];\n const matchedQty = Math.min(lot.quantity, remainingSellQty);\n\n // Fee allocation\n const allocatedBuyFeesEUR =\n lot.feesEUR * (matchedQty / lot.originalQty);\n const allocatedSellFeesEUR = tx.feesEUR * (matchedQty / tx.quantity);\n\n const buyCostEUR =\n lot.pricePerUnitEUR * matchedQty + allocatedBuyFeesEUR;\n const sellProceedsEUR =\n sellPricePerUnitEUR * matchedQty - allocatedSellFeesEUR;\n const gainLossEUR = sellProceedsEUR - buyCostEUR;\n\n matchedLots.push({\n isin: tx.isin,\n product: tx.product,\n quantity: matchedQty,\n buyDate: lot.date,\n sellDate: tx.date,\n buyPriceEUR: lot.pricePerUnitEUR,\n sellPriceEUR: sellPricePerUnitEUR,\n buyCostEUR: roundHalfUp(buyCostEUR),\n sellProceedsEUR: roundHalfUp(sellProceedsEUR),\n gainLossEUR: roundHalfUp(gainLossEUR),\n buyFxRate: lot.fxRate,\n sellFxRate: tx.fxRate,\n });\n\n lot.quantity -= matchedQty;\n remainingSellQty -= matchedQty;\n\n if (lot.quantity <= 0) {\n if (method === \"LIFO\") {\n lots.pop();\n } else {\n lots.shift();\n }\n }\n }\n }\n }\n\n // 4. Aggregate\n let plusvalenze = 0;\n let minusvalenze = 0;\n for (const lot of matchedLots) {\n if (lot.gainLossEUR > 0) plusvalenze += lot.gainLossEUR;\n else minusvalenze += Math.abs(lot.gainLossEUR);\n }\n\n // Two-bucket routing (only when classification map provided)\n if (this._options.classification) {\n const classification = this._options.classification;\n const unclassifiedIsins = new Set<string>();\n\n for (const lot of matchedLots) {\n const entry = classification[lot.isin];\n if (!entry) {\n lot.bucket = \"B\";\n unclassifiedIsins.add(lot.isin);\n } else if (entry.bucketGain === \"A\" && lot.gainLossEUR >= 0) {\n lot.bucket = \"A\";\n } else {\n lot.bucket = \"B\";\n }\n }\n\n for (const isin of unclassifiedIsins) {\n warnings.push(\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n );\n }\n\n // Bucket A computation\n const bucketALots = matchedLots.filter((l) => l.bucket === \"A\");\n const groupsByRate = new Map<\n number,\n { assetClasses: Set<string>; plusvalenze: number }\n >();\n for (const lot of bucketALots) {\n const entry = classification[lot.isin]!;\n const rate = entry.taxRate;\n if (!groupsByRate.has(rate))\n groupsByRate.set(rate, { assetClasses: new Set(), plusvalenze: 0 });\n const g = groupsByRate.get(rate)!;\n g.assetClasses.add(entry.assetClass);\n g.plusvalenze += lot.gainLossEUR;\n }\n const bucketAGroups = [...groupsByRate.entries()].map(([taxRate, g]) => ({\n taxRate,\n assetClasses: [...g.assetClasses] as AssetClass[],\n plusvalenze: roundHalfUp(g.plusvalenze),\n imposta: roundHalfUp(g.plusvalenze * taxRate),\n }));\n const bucketAReport: BucketAReport = {\n groups: bucketAGroups,\n totalImposta: roundHalfUp(\n bucketAGroups.reduce((s, g) => s + g.imposta, 0),\n ),\n };\n\n // Bucket B computation\n const bucketBLots = matchedLots.filter((l) => l.bucket === \"B\");\n let bPlusvalenze = 0;\n let bMinusvalenze = 0;\n for (const lot of bucketBLots) {\n if (lot.gainLossEUR >= 0) bPlusvalenze += lot.gainLossEUR;\n else bMinusvalenze += Math.abs(lot.gainLossEUR);\n }\n bPlusvalenze = roundHalfUp(bPlusvalenze);\n bMinusvalenze = roundHalfUp(bMinusvalenze);\n\n const carryForwards = [...(this._options.carryForward ?? [])].sort(\n (a, b) => a.year - b.year,\n );\n let remaining = bPlusvalenze - bMinusvalenze;\n let carryForwardApplied = 0;\n for (const entry of carryForwards) {\n if (taxYear - entry.year > 4) continue;\n if (remaining <= 0) break;\n const consumed = Math.min(entry.amount, remaining);\n carryForwardApplied += consumed;\n remaining -= consumed;\n }\n carryForwardApplied = roundHalfUp(carryForwardApplied);\n const bNetResult = roundHalfUp(\n bPlusvalenze - bMinusvalenze - carryForwardApplied,\n );\n const carryForwardRemaining = roundHalfUp(Math.max(0, -bNetResult));\n\n const bucketBReport: BucketBReport = {\n plusvalenze: bPlusvalenze,\n minusvalenze: bMinusvalenze,\n carryForwardApplied,\n carryForwardRemaining,\n netResult: bNetResult,\n };\n\n // Filter income rows to the tax year and build the dichiarazione report\n const allIncomeRows = this._options.incomeRows ?? [];\n const filteredIncomeRows = allIncomeRows.filter(\n (row) => new Date(row.date).getFullYear() === taxYear,\n );\n if (filteredIncomeRows.length < allIncomeRows.length) {\n warnings.push(`Income rows outside tax year ${taxYear} were skipped.`);\n }\n\n const quadroRT = buildQuadroRT(\n bucketBReport,\n this._options.carryForward ?? [],\n taxYear,\n );\n const quadroRM = buildQuadroRM(\n bucketAGroups.length > 0 ? bucketAReport : undefined,\n filteredIncomeRows,\n taxYear,\n );\n const dichiarazioneReport = buildDichiarazioneReport(\n quadroRT,\n quadroRM,\n taxYear,\n );\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n bucketA: bucketAGroups.length > 0 ? bucketAReport : undefined,\n bucketB: bucketBReport,\n dichiarazione: dichiarazioneReport,\n };\n } else {\n // Mixed-asset heuristic (no classification map)\n const allIsins = [...new Set(this._transactions.map((t) => t.isin))];\n const hasIePrefix = allIsins.some((isin) => isin.startsWith(\"IE\"));\n const hasOther = allIsins.some((isin) => !isin.startsWith(\"IE\"));\n if (hasIePrefix && hasOther) {\n warnings.push(\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n );\n }\n }\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n };\n }\n}\n","import { ClassificationError } from \"../errors.js\";\nimport type {\n AssetClass,\n ClassificationEntry,\n ClassificationMap,\n ClassifyOptions,\n Transaction,\n} from \"../types.js\";\nimport * as fs from \"node:fs\";\nimport * as https from \"node:https\";\n\ntype HttpPost = (\n url: string,\n body: string,\n timeoutMs: number,\n) => Promise<{ status: number; data: string }>;\n\nfunction httpsPost(\n url: string,\n body: string,\n timeoutMs: number,\n): Promise<{ status: number; data: string }> {\n return new Promise((resolve, reject) => {\n const u = new URL(url);\n const req = https.request(\n {\n hostname: u.hostname,\n path: u.pathname + u.search,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => {\n data += chunk;\n });\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, data }));\n },\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy();\n reject(new Error(\"timeout\"));\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\nconst GOVT_BOND_WHITELIST = new Set([\n \"IT\",\n \"DE\",\n \"FR\",\n \"AT\",\n \"BE\",\n \"NL\",\n \"ES\",\n \"PT\",\n \"FI\",\n \"IE\",\n \"LU\",\n \"GR\",\n \"SK\",\n \"SI\",\n \"LT\",\n \"LV\",\n \"EE\",\n \"MT\",\n \"CY\",\n \"US\",\n \"GB\",\n \"CH\",\n \"NO\",\n \"SE\",\n \"DK\",\n \"JP\",\n \"CA\",\n \"AU\",\n \"NZ\",\n \"SG\",\n \"HK\",\n \"KR\",\n]);\n\ntype TypeMapping = {\n assetClass: AssetClass;\n bucketGain: \"A\" | \"B\";\n bucketLoss: \"A\" | \"B\";\n taxRate: number;\n whiteListed: boolean | null;\n};\n\nconst SECURITY_TYPE_MAP: Record<string, TypeMapping> = {\n ETP: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Mutual Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Open-End Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Common Stock\": {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Equity: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Exchange Traded Commodity\": {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Corporate Bond\": {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Option: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Future: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Warrant: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CFD: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Swap: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Leverage Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Turbo Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Capital Protected Certificate\": {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\nfunction isKnownType(t: string): boolean {\n return t === \"Government Bond\" || t in SECURITY_TYPE_MAP;\n}\n\nfunction classifyByType(\n isin: string,\n securityType: string,\n product: string,\n warnings: string[],\n): ClassificationEntry {\n if (securityType === \"Government Bond\") {\n const prefix = isin.slice(0, 2).toUpperCase();\n const whiteListed = GOVT_BOND_WHITELIST.has(prefix);\n return {\n product,\n assetClass: whiteListed ? \"GovtBondWL\" : \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: whiteListed ? 0.125 : 0.26,\n whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n const mapped = SECURITY_TYPE_MAP[securityType];\n if (mapped) {\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n // Unknown type — flag for manual review\n warnings.push(\n `Unrecognized type for ${isin}: ${securityType}. Please classify manually.`,\n );\n return {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n}\n\nconst ASSET_CLASS_DEFAULTS: Record<AssetClass, TypeMapping> = {\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n Stock: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n ETC: {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n GovtBondWL: {\n assetClass: \"GovtBondWL\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.125,\n whiteListed: true,\n },\n GovtBondOther: {\n assetClass: \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: false,\n },\n CorpBond: {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Derivative: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n LeverageCert: {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CapProtectedCert: {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\n/**\n * Builds a user-confirmed `ClassificationEntry` from a caller-supplied\n * `AssetClass` override (v0.8.0 stateless mode). The caller picked the asset\n * class directly, so no ISIN-prefix whitelist lookup is performed here — the\n * caller owns gov-bond-whitelisted-vs-other correctness.\n */\nfunction buildEntryFromAssetClass(\n assetClass: AssetClass,\n product: string,\n): ClassificationEntry {\n const mapped = ASSET_CLASS_DEFAULTS[assetClass];\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: true,\n source: \"user\",\n };\n}\n\ntype OpenFIGIItem = { securityType?: string; securityType2?: string };\ntype OpenFIGIResult = { data?: OpenFIGIItem[]; error?: string };\n\nexport class Classifier {\n private readonly interactive: boolean;\n\n constructor(options?: { interactive?: boolean }) {\n this.interactive = options?.interactive ?? true;\n }\n\n async load(sidecarPath: string): Promise<ClassificationMap> {\n if (!fs.existsSync(sidecarPath)) {\n throw new ClassificationError(\"SIDECAR_NOT_FOUND\");\n }\n\n let parsed: unknown;\n try {\n const raw = fs.readFileSync(sidecarPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch {\n throw new ClassificationError(\"SIDECAR_MALFORMED\");\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>)[\"version\"] !== 1\n ) {\n throw new ClassificationError(\"SIDECAR_VERSION\");\n }\n\n return (parsed as { classifications: ClassificationMap }).classifications;\n }\n\n async classify(\n transactions: Transaction[],\n sidecarPath?: string,\n options?: ClassifyOptions,\n _httpPost: HttpPost = httpsPost,\n ): Promise<ClassificationMap> {\n // 1. Deduplicate ISINs; track first product name seen per ISIN\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n // 2. Build the \"already resolved\" set.\n // Sidecar mode (unchanged v0.6.0 behavior): only confirmedByUser:true\n // entries from disk are trusted; everything else is re-processed.\n // Stateless mode (sidecarPath undefined, v0.8.0): no disk access at\n // all — ALL entries in options.existingClassification are trusted\n // as-is, regardless of confirmedByUser.\n const confirmed: ClassificationMap = {};\n\n if (sidecarPath !== undefined) {\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await this.load(sidecarPath);\n for (const [isin, entry] of Object.entries(existingMap)) {\n if (entry.confirmedByUser) {\n confirmed[isin] = entry;\n }\n }\n }\n } else if (options?.existingClassification) {\n Object.assign(confirmed, options.existingClassification);\n }\n\n // overrides always win, in both modes, and are never sent to OpenFIGI\n for (const [isin, assetClass] of Object.entries(options?.overrides ?? {})) {\n const product = isinToProduct.get(isin) ?? isin;\n confirmed[isin] = buildEntryFromAssetClass(assetClass, product);\n }\n\n // 3. ISINs that need (re-)processing: not already resolved\n const toProcess: string[] = [];\n for (const isin of isinToProduct.keys()) {\n if (!(isin in confirmed)) {\n toProcess.push(isin);\n }\n }\n\n // 4. Batch-query OpenFIGI (skipped entirely in offline mode)\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n\n if (options?.offline) {\n for (const isin of toProcess) {\n const product = isinToProduct.get(isin) ?? isin;\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n }\n } else {\n const BATCH_SIZE = 10;\n const totalBatches = Math.ceil(toProcess.length / BATCH_SIZE);\n\n for (let i = 0; i < toProcess.length; i += BATCH_SIZE) {\n // Wait 6 s between consecutive batch requests\n if (i > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 6000));\n }\n\n const batch = toProcess.slice(i, i + BATCH_SIZE);\n const requestBody = JSON.stringify(\n batch.map((isin) => ({ idType: \"ID_ISIN\", idValue: isin })),\n );\n\n let response: { status: number; data: string } | null = null;\n\n // Try once; retry once on 5xx\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const r = await _httpPost(\n \"https://api.openfigi.com/v3/mapping\",\n requestBody,\n 10_000,\n );\n if (r.status < 500) {\n response = r;\n break;\n }\n // 5xx — loop will retry once\n } catch {\n // Network-level error — no retry\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n }\n\n if (response === null) {\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n\n const results = JSON.parse(response.data) as OpenFIGIResult[];\n\n for (let j = 0; j < batch.length; j++) {\n const isin = batch[j]!;\n const result = results[j];\n const product = isinToProduct.get(isin) ?? isin;\n\n if (\n !result ||\n result.error ||\n !result.data ||\n result.data.length === 0\n ) {\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n continue;\n }\n\n const st = result.data[0]?.securityType;\n const st2 = result.data[0]?.securityType2;\n\n // Prefer the type that resolves to a known mapping\n const typeToUse =\n st && isKnownType(st)\n ? st\n : st2 && isKnownType(st2)\n ? st2\n : (st ?? st2 ?? \"Unknown\");\n\n newEntries[isin] = classifyByType(isin, typeToUse, product, warnings);\n }\n\n const batchIndex = i / BATCH_SIZE;\n options?.onBatchProgress?.(batchIndex + 1, totalBatches);\n }\n }\n\n // 5. Merge: confirmed/overrides entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar — only in sidecar mode; stateless mode never touches disk\n if (sidecarPath !== undefined) {\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications: mergedMap,\n warnings,\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n throw new ClassificationError(\"WRITE_ERROR\");\n }\n }\n\n return mergedMap;\n }\n}\n","import type { GainsReport, AssetClass } from \"../types.js\";\nimport type { LocaleStrings } from \"../i18n/types.js\";\n\nconst SEPARATOR = \"─\".repeat(72);\n\nfunction formatEUR(amount: number, locale: string): string {\n return new Intl.NumberFormat(locale, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nfunction formatGainLoss(amount: number, locale: string): string {\n const formatted = formatEUR(Math.abs(amount), locale);\n return amount >= 0 ? `+${formatted}` : `-${formatted}`;\n}\n\nfunction assetClassLabel(ac: AssetClass, s: LocaleStrings): string {\n if (ac === \"ETF\") return s.bucketAEtf;\n if (ac === \"GovtBondWL\" || ac === \"GovtBondOther\") return s.bucketABtpWl;\n return ac;\n}\n\n/**\n * Renders the \"MODELLO REDDITI PF\" declaration section (Quadro RT + Quadro RM)\n * appended to the calc output when a classification sidecar is present.\n * See docs/prd/14-dichiarazione-engine.md \"CLI Output Section\".\n */\nfunction renderDichiarazione(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const d = report.dichiarazione!;\n const rt = d.quadroRT;\n const rm = d.quadroRM;\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n lines.push(SEPARATOR);\n lines.push(s.dichiarazioneHeader(d.annoImposta));\n lines.push(s.dichiarazioneNota);\n lines.push(\"\");\n\n // Quadro RT — Sezione II (redditi diversi)\n lines.push(s.quadroRTHeader);\n lines.push(` ${s.quadroRTPlusvalenze} ${fmt(rt.plusvalenze)}`);\n lines.push(` ${s.quadroRTMinusvalenze} ${fmt(rt.minusvalenze)}`);\n lines.push(` ${s.quadroRTDifferenza} ${fmt(rt.differenza)}`);\n for (const entry of rt.carryForwardApplied) {\n lines.push(\n ` ${s.quadroRTRiporto(entry.annoOrigine)} ${fmt(entry.importo)}`,\n );\n }\n let rtNLine = ` ${s.quadroRTImponibile} ${fmt(rt.imponibileNetto)}`;\n if (rt.differenza < 0) {\n rtNLine += ` (${s.quadroRTPerdita} ${fmt(Math.abs(rt.differenza))})`;\n }\n if (rt.differenza > 0 && !carryForwardWasProvided) {\n rtNLine += ` ${s.warnNoCarryForwardProvided}`;\n }\n lines.push(rtNLine);\n lines.push(` ${s.quadroRTImposta} ${fmt(rt.imposta)}`);\n if (rt.differenza < 0) {\n const riportato = rt.carryForwardRiportato.reduce(\n (sum, e) => sum + e.importo,\n 0,\n );\n lines.push(\n ` ${s.quadroRTRiportabile} ${fmt(riportato)} ${s.bucketBCarryNote}`,\n );\n }\n lines.push(\"\");\n\n // Quadro RM (redditi da capitale)\n lines.push(s.quadroRMHeader);\n if (rm.capitaleAliquota26.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMEtf26} ${fmt(rm.capitaleAliquota26.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A1] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota26.imposta)}`,\n );\n }\n if (rm.capitaleAliquota125.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMBtp} ${fmt(rm.capitaleAliquota125.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A2] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota125.imposta)}`,\n );\n }\n for (const div of rm.dividendiEsteri) {\n lines.push(\n ` ${s.quadroRMDividendi} — ${div.prodotto} (${div.isin}) ${fmt(div.lordo)}`,\n );\n lines.push(\n ` [RM-D] ${s.quadroRMRitenuta} — ${div.prodotto} (${div.isin}) ${fmt(div.rittenutaEstera)}`,\n );\n }\n for (const c of rm.cedole) {\n lines.push(\n ` ${s.quadroRMCedole} — ${c.prodotto} (${c.isin}) ${fmt(c.importo)}`,\n );\n }\n lines.push(\"\");\n\n lines.push(s.dichiarazioneWarningRow);\n lines.push(s.dichiarazioneDisclaimer);\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n const hasBuckets = !!(report.bucketA || report.bucketB);\n\n // Header\n lines.push(\n `${s.headerMethod}: ${report.method} | ${s.headerTaxYear}: ${report.taxYear}`,\n );\n lines.push(\"\");\n\n // Pre-table warnings (AVVISO: or WARNING:)\n for (const w of report.warnings) {\n if (w.startsWith(\"AVVISO:\") || w.startsWith(\"WARNING:\")) {\n lines.push(w);\n lines.push(\"\");\n }\n }\n\n // Column headers\n const headerCols = [\n s.headerIsin.padEnd(14),\n s.headerProduct.padEnd(20),\n s.headerQty.padStart(5),\n s.headerBuyDate.padEnd(12),\n s.headerSellDate.padEnd(12),\n s.headerBuyEur.padStart(14),\n s.headerSellEur.padStart(13),\n s.headerGainLoss.padStart(17),\n ];\n if (hasBuckets) {\n headerCols.push(s.headerBucket.padStart(8));\n }\n lines.push(headerCols.join(\" \"));\n\n // Lot rows\n for (const lot of report.lots) {\n const rowCols = [\n lot.isin.padEnd(14),\n lot.product.substring(0, 20).padEnd(20),\n String(lot.quantity).padStart(5),\n lot.buyDate.padEnd(12),\n lot.sellDate.padEnd(12),\n fmt(lot.buyCostEUR).padStart(14),\n fmt(lot.sellProceedsEUR).padStart(13),\n formatGainLoss(lot.gainLossEUR, s.numberLocale).padStart(17),\n ];\n if (hasBuckets) {\n rowCols.push((lot.bucket ?? \"\").padStart(8));\n }\n lines.push(rowCols.join(\" \"));\n }\n\n lines.push(\"\");\n lines.push(SEPARATOR);\n\n // Summary\n lines.push(`${s.summaryPlusvalenze}: ${fmt(report.plusvalenze)} EUR`);\n lines.push(`${s.summaryMinusvalenze}: ${fmt(report.minusvalenze)} EUR`);\n lines.push(`${s.summaryNetResult}: ${fmt(report.netResult)} EUR`);\n lines.push(\"\");\n lines.push(`${s.summaryWarnings}: ${report.warnings.length}`);\n lines.push(`${s.summaryGenerated}: ${report.generatedAt}`);\n lines.push(\"\");\n\n // Bucket A section\n if (report.bucketA && report.bucketA.groups.length > 0) {\n lines.push(SEPARATOR);\n lines.push(s.bucketAHeader);\n lines.push(SEPARATOR);\n for (const g of report.bucketA.groups) {\n const label = g.assetClasses\n .map((ac) => assetClassLabel(ac, s))\n .join(\", \");\n const rateDisplay = (g.taxRate * 100).toFixed(0);\n lines.push(\n `${label}: ${fmt(g.plusvalenze)} EUR → imposta: ${fmt(g.imposta)} EUR (${rateDisplay}%)`,\n );\n }\n lines.push(`${s.bucketATotalTax}: ${fmt(report.bucketA.totalImposta)} EUR`);\n lines.push(\"\");\n }\n\n // Bucket B section\n if (report.bucketB) {\n lines.push(SEPARATOR);\n lines.push(s.bucketBHeader);\n lines.push(SEPARATOR);\n lines.push(`PLUSVALENZE: ${fmt(report.bucketB.plusvalenze)} EUR`);\n lines.push(`MINUSVALENZE: ${fmt(report.bucketB.minusvalenze)} EUR`);\n if (report.bucketB.carryForwardApplied > 0) {\n lines.push(\n `${s.bucketBCarryApplied(0)}: ${fmt(report.bucketB.carryForwardApplied)} EUR`,\n );\n }\n let resultLine = `${s.bucketBResult}: ${fmt(report.bucketB.netResult)} EUR`;\n if (report.bucketB.carryForwardRemaining > 0) {\n resultLine += ` ${s.bucketBCarryNote}`;\n }\n lines.push(resultLine);\n lines.push(\"\");\n }\n\n // Mixed-buckets footnote\n if (hasBuckets) {\n lines.push(s.warnMixedBuckets);\n lines.push(\"\");\n }\n\n if (report.dichiarazione) {\n lines.push(renderDichiarazione(report, s, carryForwardWasProvided));\n } else {\n lines.push(s.disclaimer);\n }\n\n return lines.join(\"\\n\");\n}\n","import * as fs from \"node:fs\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport async function runValidate(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\"Usage: minus-tracker validate <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n stdout.write(s.validateOk(transactions.length, 0) + \"\\n\");\n\n for (const entry of parser.warningEntries) {\n let reason: string;\n switch (entry.code) {\n case \"MISSING_ISIN\":\n reason = s.warnMissingIsin(entry.row);\n break;\n case \"UNSUPPORTED_CURRENCY\":\n reason = s.warnUnsupportedCurrency(entry.row, entry.currency);\n break;\n case \"NO_ECB_RATE\":\n reason = s.warnNoEcbRate(entry.row, entry.currency, entry.date);\n break;\n case \"QUANTITY_ZERO\":\n reason = s.warnQuantityZero(entry.row);\n break;\n case \"MISSING_ISIN_INCOME\":\n reason = s.warnMissingIsinIncome(entry.row);\n break;\n case \"ORPHAN_WITHHOLDING\":\n reason = s.warnOrphanWithholding(entry.isin, entry.date);\n break;\n }\n stdout.write(reason + \"\\n\");\n }\n\n return 0;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport * as https from \"node:https\";\nimport { getActiveSnapshot, type RatesSnapshot } from \"../../rates/index.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport function getSnapshotPath(): string {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n }\n return path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n}\n\nfunction getCoverage(snapshot: RatesSnapshot): {\n start: string;\n end: string;\n currencies: string;\n} {\n let start = \"9999-12-31\";\n let end = \"0000-01-01\";\n const currencies: string[] = [];\n\n for (const [ccy, dates] of Object.entries(snapshot)) {\n currencies.push(ccy);\n for (const d of Object.keys(dates)) {\n if (d < start) start = d;\n if (d > end) end = d;\n }\n }\n return { start, end, currencies: currencies.sort().join(\", \") };\n}\n\nexport async function fetchEcbData(\n currency: string,\n): Promise<Record<string, number>> {\n return new Promise((resolve, reject) => {\n const url = `https://data-api.ecb.europa.eu/service/data/EXR/D.${currency}.EUR.SP00.A?format=csvdata&startPeriod=2019-01-01`;\n https\n .get(url, (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => (data += chunk));\n res.on(\"end\", () => {\n const rates: Record<string, number> = {};\n const lines = data.split(\"\\n\");\n // ECB SDMX csvdata format:\n // KEY,FREQ,CURRENCY,CURRENCY_DENOM,EXR_TYPE,EXR_SUFFIX,TIME_PERIOD,OBS_VALUE,...\n // DATE is column index 6, rate is column index 7\n for (const line of lines) {\n const parts = line.split(\",\");\n if (parts.length < 8) continue;\n const date = parts[6].trim();\n const rate = parseFloat(parts[7].trim());\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date) && !isNaN(rate) && rate > 0) {\n rates[date] = rate;\n }\n }\n resolve(rates);\n });\n res.on(\"error\", reject);\n })\n .on(\"error\", reject);\n });\n}\n\nexport async function updateRates(\n snapshotPath: string,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n s: LocaleStrings,\n): Promise<void> {\n stdout.write(s.ratesUpdateFetching + \"\\n\");\n\n let existing: RatesSnapshot = {};\n try {\n existing = JSON.parse(\n fs.readFileSync(snapshotPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n /* file doesn't exist yet */\n }\n\n let addedCount = 0;\n for (const currency of [\"USD\", \"GBP\", \"CHF\"]) {\n try {\n const newRates = await fetchEcbData(currency);\n const existing_ccy = existing[currency] ?? {};\n let added = 0;\n for (const [date, rate] of Object.entries(newRates)) {\n if (!existing_ccy[date]) added++;\n existing_ccy[date] = rate;\n }\n existing[currency] = existing_ccy;\n addedCount += added;\n } catch {\n stderr.write(`Failed to fetch ${currency} rates\\n`);\n }\n }\n\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n fs.writeFileSync(\n snapshotPath,\n JSON.stringify(existing, null, 2) + \"\\n\",\n \"utf8\",\n );\n stdout.write(s.ratesUpdateDone(addedCount) + \"\\n\");\n stdout.write(s.ratesSnapshotWritten(snapshotPath) + \"\\n\");\n}\n\nexport async function runRates(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"check\"]) {\n const snapshot = getActiveSnapshot();\n const { start, end, currencies } = getCoverage(snapshot);\n stdout.write(s.ratesCoverage(start, end, currencies) + \"\\n\");\n stdout.write(s.ratesGapsNone + \"\\n\");\n return 0;\n }\n\n if (flags[\"update\"]) {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker rates --check | --update\\n\");\n return 2;\n}\n","import { resolveLocale, saveLocale } from \"../../i18n/settings.js\";\nimport type { LocaleStrings, SupportedLocale } from \"../../i18n/types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport async function runConfig(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"lang\"] !== undefined) {\n const lang = flags[\"lang\"] as string;\n if (!SUPPORTED_LOCALES.includes(lang as SupportedLocale)) {\n stderr.write(`Unsupported locale \"${lang}\". Use: it, en\\n`);\n return 2;\n }\n saveLocale(lang as SupportedLocale);\n stdout.write(s.configLangSet(lang as SupportedLocale) + \"\\n\");\n return 0;\n }\n\n if (flags[\"show\"]) {\n const locale = resolveLocale();\n stdout.write(s.configCurrentLang(locale) + \"\\n\");\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker config --lang <it|en> | --show\\n\");\n return 2;\n}\n","/**\n * stress-test CLI command handler\n * Runs all (or a range of) stress test scenarios against the CLI binary.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSync } from \"node:child_process\";\nimport { generateCsv } from \"../../stress/generator.js\";\nimport { runScenario } from \"../../stress/runner.js\";\nimport { buildReport, formatTable, formatJson } from \"../../stress/reporter.js\";\nimport type { StressManifest } from \"../../stress/generator.js\";\n\nexport async function runStressTest(\n _positionals: string[],\n flags: Record<string, string | boolean>,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n // 1. Parse --range flag\n const rangeStr = (flags[\"range\"] as string | undefined) ?? \"1-100\";\n const rangeParts = rangeStr.split(\"-\");\n if (rangeParts.length !== 2) {\n stderr.write(\n `Error: --range must be in the form N-M (e.g. 1-100), got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n const rangeStart = parseInt(rangeParts[0], 10);\n const rangeEnd = parseInt(rangeParts[1], 10);\n if (\n isNaN(rangeStart) ||\n isNaN(rangeEnd) ||\n rangeStart <= 0 ||\n rangeEnd <= 0 ||\n rangeStart > rangeEnd ||\n rangeStart > 100 ||\n rangeEnd > 100\n ) {\n stderr.write(\n `Error: --range values must be positive integers between 1 and 100 with start ≤ end, got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n\n // 2. Resolve manifest path\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const manifestPath = path.join(__dirname, \"../data/stress-manifest.json\");\n\n // 3. Load manifest\n let manifest: StressManifest;\n try {\n const raw = fs.readFileSync(manifestPath, \"utf8\");\n manifest = JSON.parse(raw) as StressManifest;\n } catch {\n stderr.write(`Error: could not load stress manifest at ${manifestPath}\\n`);\n return 1;\n }\n\n // 4. Resolve CLI binary\n const cliBin = process.argv[1];\n\n // 5. Create output directory\n let outputDir: string;\n const outputDirFlag = flags[\"output-dir\"] as string | undefined;\n if (outputDirFlag) {\n outputDir = outputDirFlag;\n fs.mkdirSync(outputDir, { recursive: true });\n } else {\n outputDir = fs.mkdtempSync(path.join(os.tmpdir(), \"minus-tracker-stress-\"));\n }\n\n // 8. Run global checks before scenarios\n const ratesCheck = spawnSync(\"node\", [cliBin, \"rates\", \"--check\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((ratesCheck.status ?? 1) !== 0) {\n stderr.write(\"Warning: rates --check failed; ECB rates may be stale.\\n\");\n }\n\n const configShow = spawnSync(\"node\", [cliBin, \"config\", \"--show\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((configShow.status ?? 1) !== 0) {\n stderr.write(\"Warning: config --show failed.\\n\");\n }\n\n // 6. Filter scenarios by range\n const scenarios = manifest.scenarios.filter((s) => {\n const id = parseInt(s.id, 10);\n return id >= rangeStart && id <= rangeEnd;\n });\n\n // 7. Run each scenario\n const results = [];\n for (const scenario of scenarios) {\n const csv = generateCsv(scenario);\n const result = runScenario(scenario, csv, outputDir, cliBin);\n results.push(result);\n stdout.write(\n ` [${result.pass ? \"✓\" : \"✗\"}] ${scenario.id} ${scenario.slug}\\n`,\n );\n }\n\n // 9. Build and output report\n const report = buildReport(results, rangeStart, rangeEnd);\n if (flags[\"json\"]) {\n stdout.write(formatJson(report) + \"\\n\");\n } else {\n stdout.write(formatTable(report) + \"\\n\");\n }\n\n // 10. Cleanup unless --keep\n if (!flags[\"keep\"]) {\n fs.rmSync(outputDir, { recursive: true, force: true });\n }\n\n // 11. Return exit code\n return report.failed === 0 ? 0 : 1;\n}\n","/**\n * DEGIRO CSV Generator for stress testing\n * Converts manifest scenarios into DEGIRO-format CSV strings.\n */\n\n/**\n * A single transaction from the manifest\n */\nexport interface ManifestTransaction {\n date: string; // YYYY-MM-DD\n product: string;\n isin: string;\n qty: number; // positive = BUY, negative = SELL\n price: number;\n currency: string; // EUR | USD | GBP | CHF\n fee: number; // absolute EUR fee\n}\n\n/**\n * Expected outcomes for a scenario\n */\nexport interface ManifestExpect {\n calc_exit: number;\n fifo_exit: number;\n json_exit: number;\n en_exit: number;\n validate_exit: number;\n warning_count: number;\n}\n\n/**\n * A single test scenario\n */\nexport interface ManifestScenario {\n id: string;\n category: string;\n slug: string;\n description: string;\n transactions: ManifestTransaction[];\n expect: ManifestExpect;\n special?: string;\n}\n\n/**\n * The full stress test manifest\n */\nexport interface StressManifest {\n version: string;\n ecb_reference_dates: Record<string, unknown>;\n scenarios: ManifestScenario[];\n}\n\n/**\n * Convert a date from YYYY-MM-DD to DD-MM-YYYY\n */\nfunction formatDate(isoDate: string): string {\n const [yyyy, mm, dd] = isoDate.split(\"-\");\n return `${dd}-${mm}-${yyyy}`;\n}\n\n/**\n * Generate a DEGIRO-format CSV string for a given scenario.\n *\n * DEGIRO CSV format (18 columns, exact order):\n * Date,Time,Product,ISIN,Exchange,Execution centre,Quantity,Price,Local value,\n * Local value currency,Value,Value currency,Exchange rate,Transaction costs,\n * Transaction costs currency,Total,Total currency,Order ID\n *\n * Returns the CSV content as a string (header + data rows, joined by \\n)\n */\nexport function generateCsv(scenario: ManifestScenario): string {\n // Handle special cases\n if (scenario.special === \"invalid_csv\") {\n // Return garbage bytes (invalid CSV)\n return \"NOT_A_CSV\\x00\\x01\\x02\\ngarbage data\";\n }\n\n // For normal and column-removal cases, build the header first\n const allColumns = [\n \"Date\",\n \"Time\",\n \"Product\",\n \"ISIN\",\n \"Exchange\",\n \"Execution centre\",\n \"Quantity\",\n \"Price\",\n \"Local value\",\n \"Local value currency\",\n \"Value\",\n \"Value currency\",\n \"Exchange rate\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Total\",\n \"Total currency\",\n \"Order ID\",\n ];\n\n // Remove columns based on special field\n let columns = allColumns;\n if (scenario.special === \"missing_col_isin\") {\n columns = columns.filter((c) => c !== \"ISIN\");\n } else if (scenario.special === \"missing_col_quantity\") {\n columns = columns.filter((c) => c !== \"Quantity\");\n } else if (scenario.special === \"missing_col_price\") {\n columns = columns.filter((c) => c !== \"Price\");\n }\n\n // Helper to build a CSV row, omitting columns as needed\n const buildRow = (\n values: Record<string, string>,\n includeColumns: string[],\n ): string => {\n return includeColumns.map((col) => values[col] ?? \"\").join(\",\");\n };\n\n // Build header\n const header = columns.join(\",\");\n const rows = [header];\n\n // Build data rows\n for (let rowIndex = 0; rowIndex < scenario.transactions.length; rowIndex++) {\n const tx = scenario.transactions[rowIndex];\n\n // Calculate local value: -qty * price\n // For BUY (qty > 0): localValue is negative (money out)\n // For SELL (qty < 0): localValue is positive (money in)\n const localValue = -tx.qty * tx.price;\n const total = localValue - tx.fee;\n\n const rowValues: Record<string, string> = {\n Date: formatDate(tx.date),\n Time: \"09:00\",\n Product: tx.product,\n ISIN: tx.isin,\n Exchange: \"XEUR\",\n \"Execution centre\": \"XEUR\",\n Quantity: String(tx.qty),\n Price: tx.price.toFixed(2),\n \"Local value\": localValue.toFixed(2),\n \"Local value currency\": tx.currency,\n Value: localValue.toFixed(2),\n \"Value currency\": \"EUR\",\n \"Exchange rate\": \"1\",\n \"Transaction costs\": (-Math.abs(tx.fee)).toFixed(2),\n \"Transaction costs currency\": \"EUR\",\n Total: total.toFixed(2),\n \"Total currency\": \"EUR\",\n \"Order ID\": `stress-${scenario.id}-${rowIndex}`,\n };\n\n const row = buildRow(rowValues, columns);\n rows.push(row);\n }\n\n return rows.join(\"\\n\");\n}\n","/**\n * CLI runner for minus-tracker stress test scenarios\n * Executes CLI commands against generated CSV files and collects results.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ManifestScenario } from \"./generator.js\";\n\n/**\n * Result of running a single CLI command\n */\nexport interface CommandResult {\n cmd: string;\n exitCode: number;\n stdout: string;\n stderr: string;\n pass: boolean;\n failure?: string;\n}\n\n/**\n * Result of running all commands for a scenario\n */\nexport interface ScenarioResult {\n id: string;\n category: string;\n slug: string;\n description: string;\n csvFile: string;\n results: CommandResult[];\n pass: boolean;\n warningCheckPass: boolean;\n expectedWarningCount: number;\n actualWarningCount: number;\n}\n\n/**\n * Extract the warning count from calc output.\n * The English renderer emits \"WARNINGS: N\" and the Italian renderer emits \"AVVERTENZE: N\".\n * We parse that numeric value directly rather than counting matching lines.\n */\nfunction countWarnings(text: string): number {\n if (!text) return 0;\n const match = text.match(/(?:warnings|avvertenze)\\s*:\\s*(\\d+)/i);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n/**\n * Check if a string is valid JSON with required keys\n */\nfunction isValidJson(jsonStr: string): boolean {\n try {\n const obj = JSON.parse(jsonStr);\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"plusvalenze\" in obj &&\n \"minusvalenze\" in obj &&\n \"lots\" in obj\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Run a single CLI command and return its result\n */\nfunction runCommand(\n cliBin: string,\n args: string[],\n cmdLabel: string,\n expectedExitCode: number,\n outputShapeCheck: (\n stdout: string,\n stderr: string,\n exitCode: number,\n ) => { valid: boolean; reason?: string },\n): CommandResult {\n // Determine how to invoke the CLI\n const isJsFile = cliBin.endsWith(\".js\");\n let result;\n\n if (isJsFile) {\n // Run: node <cliBin> <args>\n result = spawnSync(\"node\", [cliBin, ...args], {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n } else {\n // Run: <cliBin> <args> (directly executable)\n result = spawnSync(cliBin, args, {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n }\n\n const exitCode = result.status ?? 1; // null status (timeout/error) → treat as 1\n const stdout = result.stdout || \"\";\n const stderr = result.stderr || \"\";\n\n // Check exit code match\n const exitCodeMatch = exitCode === expectedExitCode;\n\n // Check output shape\n const shapeCheck = outputShapeCheck(stdout, stderr, exitCode);\n\n let pass = exitCodeMatch && shapeCheck.valid;\n let failure: string | undefined;\n\n if (!exitCodeMatch) {\n failure = `exit code mismatch: expected ${expectedExitCode}, got ${exitCode}`;\n } else if (!shapeCheck.valid) {\n failure = shapeCheck.reason;\n }\n\n return {\n cmd: cmdLabel,\n exitCode,\n stdout,\n stderr,\n pass,\n failure,\n };\n}\n\n/**\n * Run all CLI commands for a scenario and return the result\n */\nexport function runScenario(\n scenario: ManifestScenario,\n csvContent: string,\n outputDir: string,\n cliBin: string,\n): ScenarioResult {\n // Write CSV to temp file\n const csvFileName = `${scenario.id}-${scenario.slug}.csv`;\n const csvFile = join(outputDir, csvFileName);\n writeFileSync(csvFile, csvContent, \"utf8\");\n\n const results: CommandResult[] = [];\n\n // Run calc command (LIFO)\n results.push(\n runCommand(\n cliBin,\n [\"calc\", csvFile],\n \"calc\",\n scenario.expect.calc_exit,\n (stdout) => {\n if (scenario.expect.calc_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --method FIFO\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--method\", \"FIFO\", csvFile],\n \"calc --method FIFO\",\n scenario.expect.fifo_exit,\n (stdout) => {\n if (scenario.expect.fifo_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --method FIFO output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --json\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--json\", csvFile],\n \"calc --json\",\n scenario.expect.json_exit,\n (stdout) => {\n if (scenario.expect.json_exit === 0) {\n const isValid = isValidJson(stdout);\n return isValid\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --json output is not valid JSON with required keys (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --lang en\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--lang\", \"en\", csvFile],\n \"calc --lang en\",\n scenario.expect.en_exit,\n (stdout) => {\n if (scenario.expect.en_exit === 0) {\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc --lang en output is empty (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run validate\n results.push(\n runCommand(\n cliBin,\n [\"validate\", csvFile],\n \"validate\",\n scenario.expect.validate_exit,\n (stdout) => {\n if (scenario.expect.validate_exit !== 0) return { valid: true };\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"validate output is empty\",\n };\n },\n ),\n );\n\n // Count warnings using English locale calc output (immune to locale config)\n // English output emits \"WARNINGS: N\" which matches the /warn/i pattern reliably\n const enCalcResult = results[3]; // calc --lang en is always index 3\n const actualWarningCount = countWarnings(enCalcResult.stdout);\n const warningCheckPass = actualWarningCount >= scenario.expect.warning_count;\n\n // Determine overall pass\n const pass = results.every((r) => r.pass) && warningCheckPass;\n\n return {\n id: scenario.id,\n category: scenario.category,\n slug: scenario.slug,\n description: scenario.description,\n csvFile,\n results,\n pass,\n warningCheckPass,\n expectedWarningCount: scenario.expect.warning_count,\n actualWarningCount,\n };\n}\n","/**\n * Reporter for minus-tracker stress test suite.\n * Formats stress test results into human-readable table + summary.\n */\n\nimport type { ScenarioResult } from \"./runner.js\";\n\n/**\n * Aggregated stress test report\n */\nexport interface StressReport {\n totalScenarios: number;\n passed: number;\n failed: number;\n results: ScenarioResult[];\n rangeStart: number; // 1-based\n rangeEnd: number;\n}\n\n/**\n * Aggregates a list of ScenarioResult into a StressReport\n */\nexport function buildReport(\n results: ScenarioResult[],\n rangeStart: number,\n rangeEnd: number,\n): StressReport {\n const passed = results.filter((r) => r.pass).length;\n const failed = results.length - passed;\n\n return {\n totalScenarios: results.length,\n passed,\n failed,\n results,\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * Format a ScenarioResult as a JSON string\n */\nexport function formatJson(report: StressReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Format a StressReport as a human-readable ASCII table\n */\nexport function formatTable(report: StressReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"STRESS TEST — minus-tracker\");\n lines.push(\n `Scenari: ${report.totalScenarios} | Passati: ${report.passed} | Falliti: ${report.failed}`,\n );\n lines.push(\"\");\n\n // Column header\n const idCol = \"ID\";\n const categoryCol = \"CATEGORIA\";\n const descCol = \"DESCRIZIONE\";\n const resultCol = \"ESITO\";\n\n lines.push(formatRow(idCol, categoryCol, descCol, resultCol));\n\n // Rows for each scenario\n for (const result of report.results) {\n const idStr = result.id.padStart(3, \"0\");\n const status = result.pass ? \"✓ PASS\" : \"✗ FAIL\";\n\n lines.push(formatRow(idStr, result.category, result.description, status));\n\n // If failed, add failure details\n if (!result.pass) {\n for (const cmdResult of result.results) {\n if (!cmdResult.pass) {\n if (cmdResult.failure) {\n let failureText = \"\";\n\n // Check for exit code mismatch pattern\n if (cmdResult.failure.includes(\"exit code mismatch\")) {\n const match = cmdResult.failure.match(\n /expected (\\d+), got (\\d+)/,\n );\n if (match) {\n failureText = `atteso exit ${match[1]}, ottenuto exit ${match[2]}`;\n }\n } else {\n // Output shape check failed\n failureText = `output shape non valido — ${cmdResult.failure}`;\n }\n\n lines.push(` → ${cmdResult.cmd}: ${failureText}`);\n }\n }\n }\n\n // Check warning count\n if (!result.warningCheckPass) {\n lines.push(\n ` → avvertenze: attese ≥${result.expectedWarningCount}, trovate ${result.actualWarningCount}`,\n );\n }\n }\n }\n\n // Summary footer\n lines.push(\"\");\n lines.push(`PASSATI: ${report.passed}`);\n lines.push(`FALLITI: ${report.failed}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single table row with fixed column widths\n */\nfunction formatRow(\n id: string,\n category: string,\n description: string,\n esito: string,\n): string {\n // Column widths: ID=4, CATEGORIA=22, DESCRIZIONE=40, ESITO=8\n const idWidth = 4;\n const categoryWidth = 22;\n const descWidth = 40;\n const esitoWidth = 8;\n\n // Pad/truncate columns\n const idCol = id.padEnd(idWidth);\n const categoryCol = truncateOrPad(category, categoryWidth);\n const descCol = truncateOrPad(description, descWidth);\n const esitoCol = esito.padEnd(esitoWidth);\n\n return `${idCol} ${categoryCol} ${descCol} ${esitoCol}`;\n}\n\n/**\n * Truncate a string to max width with \"..\" suffix, or pad to width\n */\nfunction truncateOrPad(str: string, maxWidth: number): string {\n if (str.length > maxWidth) {\n return str.slice(0, maxWidth - 2) + \"..\";\n }\n return str.padEnd(maxWidth);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type { ClassificationMap } from \"../../types.js\";\n\nexport async function runClassify(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const offline = Boolean(flags[\"offline\"]);\n\n // TTY check — FIRST, before any file I/O\n if (!process.stdin.isTTY && !offline) {\n stderr.write(s.classifyNonTtyError + \"\\n\");\n return 2;\n }\n\n const csvPath = positional[0];\n if (!csvPath) {\n stderr.write(\"Usage: minus-tracker classify [--offline] <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(csvPath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${csvPath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n // Derive sidecar path: replace .csv extension with .classify.json\n const base = csvPath.replace(/\\.csv$/i, \"\");\n const sidecarPath = base + \".classify.json\";\n\n if (offline) {\n // Offline mode: skip OpenFIGI; write stub user-confirmed entries for each unique ISIN\n stdout.write(s.classifyOfflineWarning + \"\\n\");\n\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n const classifications: ClassificationMap = {};\n for (const [isin, product] of isinToProduct.entries()) {\n classifications[isin] = {\n product,\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n confirmedByUser: true,\n source: \"user\",\n };\n }\n\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications,\n warnings: [],\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n stderr.write(`Cannot write sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n\n stdout.write(s.classifyWritten(sidecarPath) + \"\\n\");\n return 0;\n }\n\n // Online interactive mode\n const classifier = new Classifier({ interactive: true });\n\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await classifier.load(sidecarPath);\n const confirmedCount = Object.values(existingMap).filter(\n (e) => e.confirmedByUser,\n ).length;\n stdout.write(s.classifyMergePrompt(confirmedCount));\n\n const answer = await readLine(process.stdin);\n if (answer.trim().toLowerCase() === \"n\") {\n return 0;\n }\n }\n\n await classifier.classify(transactions, sidecarPath);\n stdout.write(s.classifyWritten(path.resolve(sidecarPath)) + \"\\n\");\n return 0;\n}\n\nfunction readLine(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve) => {\n let buf = \"\";\n const onData = (chunk: Buffer | string) => {\n buf += chunk.toString();\n const nl = buf.indexOf(\"\\n\");\n if (nl !== -1) {\n stream.removeListener(\"data\", onData);\n resolve(buf.slice(0, nl));\n }\n };\n stream.on(\"data\", onData);\n });\n}\n"],"mappings":"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","names":["path","path","fs","path","os","path","fs","get","isoDate","path","roundHalfUp","fs","resolve","fs","fs","path","os","https","resolve","SUPPORTED_LOCALES","fs","path","os","fileURLToPath","spawnSync","writeFileSync","join","fileURLToPath","spawnSync","fs","path","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/i18n/it.ts","../../src/i18n/en.ts","../../src/i18n/settings.ts","../../src/i18n/index.ts","../../src/errors.ts","../../src/cli/commands/calc.ts","../../src/rates/index.ts","../../src/parser/warnings.ts","../../src/parser/index.ts","../../src/dichiarazione/engine.ts","../../src/calculator/index.ts","../../src/classifier/index.ts","../../src/cli/renderer.ts","../../src/cli/commands/validate.ts","../../src/cli/commands/rates.ts","../../src/cli/commands/config.ts","../../src/cli/commands/stress-test.ts","../../src/stress/generator.ts","../../src/stress/runner.ts","../../src/stress/reporter.ts","../../src/cli/commands/classify.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolveLocale, getStrings } from \"../i18n/index.js\";\nimport { ParseError, CalculationError } from \"../errors.js\";\nimport { runCalc } from \"./commands/calc.js\";\nimport { runValidate } from \"./commands/validate.js\";\nimport { runRates } from \"./commands/rates.js\";\nimport { runConfig } from \"./commands/config.js\";\nimport { runStressTest } from \"./commands/stress-test.js\";\nimport { runClassify } from \"./commands/classify.js\";\nimport { ClassificationError } from \"../errors.js\";\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n lang: { type: \"string\" },\n method: { type: \"string\" },\n year: { type: \"string\" },\n json: { type: \"boolean\", default: false },\n check: { type: \"boolean\", default: false },\n update: { type: \"boolean\", default: false },\n show: { type: \"boolean\", default: false },\n range: { type: \"string\" },\n keep: { type: \"boolean\", default: false },\n \"output-dir\": { type: \"string\" },\n offline: { type: \"boolean\", default: false },\n \"carry-forward\": { type: \"string\", multiple: true },\n \"export-dichiarazione\": { type: \"string\" },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // Resolve locale once — exits 2 on invalid value\n const locale = resolveLocale(values.lang as string | undefined);\n const s = getStrings(locale);\n\n const command = positionals[0];\n const restPositionals = positionals.slice(1);\n const flags = values as Record<string, string | boolean>;\n\n const stdout = process.stdout;\n const stderr = process.stderr;\n\n let exitCode = 0;\n\n try {\n switch (command) {\n case \"calc\":\n exitCode = await runCalc(restPositionals, flags, s, stdout, stderr);\n break;\n case \"validate\":\n exitCode = await runValidate(restPositionals, flags, s, stdout, stderr);\n break;\n case \"rates\":\n exitCode = await runRates(restPositionals, flags, s, stdout, stderr);\n break;\n case \"config\":\n exitCode = await runConfig(restPositionals, flags, s, stdout, stderr);\n break;\n case \"stress-test\":\n exitCode = await runStressTest(restPositionals, flags, stdout, stderr);\n break;\n case \"classify\":\n exitCode = await runClassify(restPositionals, flags, s, stdout, stderr);\n break;\n default:\n stderr.write(\n \"Usage: minus-tracker <calc|validate|rates|config|stress-test|classify> [options] [file]\\n\",\n );\n exitCode = 2;\n }\n } catch (err) {\n if (err instanceof ClassificationError) {\n stderr.write(err.message + \"\\n\");\n exitCode = 1;\n } else if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n exitCode = 1;\n } else if (err instanceof CalculationError) {\n stderr.write(s.errorNoOpenLots(err.isin, err.date) + \"\\n\");\n exitCode = 1;\n } else {\n throw err;\n }\n }\n\n process.exit(exitCode);\n}\n\nmain().catch((err) => {\n process.stderr.write(`Unhandled error: ${String(err)}\\n`);\n process.exit(1);\n});\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const it: LocaleStrings = {\n numberLocale: \"it-IT\",\n\n errorInvalidCsv: \"CSV non valido: impossibile analizzare il file\",\n errorMissingColumn: (col) => `Colonna obbligatoria mancante: ${col}`,\n errorNoOpenLots: (isin, date) =>\n `Nessun lotto aperto per ISIN ${isin} in data ${date}`,\n errorCannotReadFile: (path) => `Impossibile leggere il file: ${path}`,\n errorCannotLoadSidecar: (path) => `Impossibile caricare il sidecar: ${path}`,\n errorCannotWriteExport: (path) =>\n `Impossibile scrivere l'export della dichiarazione: ${path}`,\n\n warnMissingIsin: (row) => `Riga ${row}: ISIN mancante — riga ignorata`,\n warnUnsupportedCurrency: (row, currency) =>\n `Riga ${row}: valuta non supportata ${currency} — riga ignorata`,\n warnNoEcbRate: (row, currency, date) =>\n `Riga ${row}: nessun tasso BCE per ${currency} in data ${date} — riga ignorata`,\n warnQuantityZero: (row) => `Riga ${row}: quantità pari a 0 — riga ignorata`,\n warnMissingIsinIncome: (row) =>\n `Riga ${row}: ISIN mancante su riga di reddito — riga ignorata`,\n warnOrphanWithholding: (isin, date) =>\n `Riga di ritenuta su ${isin}/${date} senza riga di reddito corrispondente — riga ignorata`,\n\n warnMultipleYears:\n \"Il CSV contiene transazioni di più anni — filtra per un singolo anno per un calcolo accurato.\",\n\n headerMethod: \"METODO\",\n headerTaxYear: \"ANNO FISCALE\",\n headerIsin: \"ISIN\",\n headerProduct: \"TITOLO\",\n headerQty: \"QTÀ\",\n headerBuyDate: \"DATA ACQUISTO\",\n headerSellDate: \"DATA VENDITA\",\n headerBuyEur: \"ACQUISTO EUR\",\n headerSellEur: \"VENDITA EUR\",\n headerGainLoss: \"GUADAGNO/PERDITA\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"RISULTATO NETTO\",\n summaryWarnings: \"AVVERTENZE\",\n summaryGenerated: \"Generato\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transazioni analizzate, ${errors} errori gravi`,\n validateWarn: (count, reason) =>\n `AVVISO: ${count} righe ignorate (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Copertura: ${start} → ${end} | Valute: ${currencies}`,\n ratesGapsNone: \"Lacune: nessuna\",\n ratesGaps: (list) => `Lacune: ${list}`,\n ratesUpdateFetching: \"Recupero dati BCE SDMX in corso...\",\n ratesUpdateDone: (n) => `Completato. Aggiunte ${n} nuove date.`,\n ratesSnapshotWritten: (path) => `Snapshot scritto in ${path}`,\n ratesAutoUpdateStart:\n \"Le rate BCE non sono aggiornate. Aggiornamento automatico in corso...\",\n ratesAutoUpdateFailed:\n \"Aggiornamento automatico fallito. Si procede con le rate disponibili.\",\n\n configLangSet: (lang) => `Lingua impostata su: ${lang}`,\n configCurrentLang: (lang) => `Lingua corrente: ${lang}`,\n\n classifyStarting: (n) => `Classificazione ISIN in corso... (${n} strumenti)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Rilevato: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Tipo di strumento:\",\n classifyDone: (confirmed, total) =>\n `Classificazione completata (${confirmed}/${total} confermati).`,\n classifyWritten: (path) => `Scritto: ${path}`,\n classifyMergePrompt: (existing) =>\n `File già esistente (${existing} ISIN confermati). Aggiungere i nuovi ISIN? [Y/n]: `,\n classifyOfflineWarning: \"Modalità offline: ricerca OpenFIGI saltata.\",\n classifyUnknownType: (type) =>\n `Tipo non riconosciuto: ${type}. Classificare manualmente.`,\n classifyNonTtyError:\n \"Modalità interattiva richiede un terminale (TTY). Usa --offline per classificare senza terminale.\",\n\n bucketAHeader: \"BUCKET A — REDDITI DA CAPITALE (non compensabili)\",\n bucketBHeader: \"BUCKET B — REDDITI DIVERSI\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"BTP/WL\",\n bucketATotalTax: \"TOTALE IMPOSTA\",\n bucketBCarryApplied: (year) => `RIPORTO ${year}`,\n bucketBResult: \"RISULTATO\",\n bucketBCarryNote: \"(riportabile ai prossimi 4 anni)\",\n warnMixedBuckets:\n \"Le perdite in Bucket B non compensano le plusvalenze in Bucket A.\",\n warnMixedAssets:\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} non trovato nella mappa di classificazione — assegnato a Bucket B.`,\n carryForwardInvalidFormat:\n \"Formato --carry-forward non valido. Usa: AAAA:importo (es. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) =>\n `── MODELLO REDDITI PF [ANNO D'IMPOSTA: ${anno}]`,\n dichiarazioneNota:\n \"Nota: i valori riportano i codici riga indicativi — verificare la modulistica ufficiale.\",\n dichiarazioneWarningRow:\n \"⚠ I codici riga tra parentesi quadre sono indicativi. Verificare la versione corrente del modello.\",\n quadroRTHeader: \"QUADRO RT — SEZIONE II (redditi diversi)\",\n quadroRTPlusvalenze: \"[RT-P] Plusvalenze totali\",\n quadroRTMinusvalenze: \"[RT-M] Minusvalenze totali\",\n quadroRTDifferenza: \"[RT-D] Differenza\",\n quadroRTRiporto: (anno) => `[RT-C*] Riporto ${anno} (consumato)`,\n quadroRTImponibile: \"[RT-N] Imponibile netto\",\n quadroRTImposta: \"[RT-I] Imposta (26%)\",\n quadroRTRiportabile: \"[RT-R] Minusvalenze da riportare\",\n quadroRTPerdita: \"perdita netta:\",\n quadroRMHeader: \"QUADRO RM (redditi da capitale)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR plusvalenze (26%)\",\n quadroRMImposta: \"Imposta sostitutiva\",\n quadroRMBtp: \"[RM-A2] BTP/WL plusvalenze (12,5%)\",\n quadroRMDividendi: \"[RM-D] Dividendi esteri lordi\",\n quadroRMRitenuta: \"Ritenuta estera (credito)\",\n quadroRMCedole: \"[RM-C] Cedole obbligazionarie\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar:\n \"Suggerimento: esegui prima 'minus-tracker classify <file>' per abilitare il Quadro RT/RM.\",\n warnNoCarryForwardProvided: \"(eventuali perdite pregresse non applicate)\",\n};\n","import type { LocaleStrings } from \"./types.js\";\n\nexport const en: LocaleStrings = {\n numberLocale: \"en-US\",\n\n errorInvalidCsv: \"Invalid CSV: unable to parse\",\n errorMissingColumn: (col) => `Missing required column: ${col}`,\n errorNoOpenLots: (isin, date) => `No open lots for ISIN ${isin} on ${date}`,\n errorCannotReadFile: (path) => `Cannot read file: ${path}`,\n errorCannotLoadSidecar: (path) => `Cannot load sidecar: ${path}`,\n errorCannotWriteExport: (path) =>\n `Cannot write dichiarazione export: ${path}`,\n\n warnMissingIsin: (row) => `Row ${row}: missing ISIN — skipped`,\n warnUnsupportedCurrency: (row, currency) =>\n `Row ${row}: unsupported currency ${currency} — skipped`,\n warnNoEcbRate: (row, currency, date) =>\n `Row ${row}: no ECB rate for ${currency} on ${date} — skipped`,\n warnQuantityZero: (row) => `Row ${row}: quantity is 0 — skipped`,\n warnMissingIsinIncome: (row) =>\n `Row ${row}: income row has blank ISIN — skipped`,\n warnOrphanWithholding: (isin, date) =>\n `Withholding row on ${isin}/${date} has no matching income row — skipped`,\n\n warnMultipleYears:\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n\n headerMethod: \"METHOD\",\n headerTaxYear: \"TAX YEAR\",\n headerIsin: \"ISIN\",\n headerProduct: \"PRODUCT\",\n headerQty: \"QTY\",\n headerBuyDate: \"BUY DATE\",\n headerSellDate: \"SELL DATE\",\n headerBuyEur: \"BUY EUR\",\n headerSellEur: \"SELL EUR\",\n headerGainLoss: \"GAIN/LOSS\",\n\n summaryPlusvalenze: \"PLUSVALENZE\",\n summaryMinusvalenze: \"MINUSVALENZE\",\n summaryNetResult: \"NET RESULT\",\n summaryWarnings: \"WARNINGS\",\n summaryGenerated: \"Generated\",\n\n validateOk: (count, errors) =>\n `OK: ${count} transactions parsed, ${errors} hard errors`,\n validateWarn: (count, reason) => `WARN: ${count} rows skipped (${reason})`,\n\n ratesCoverage: (start, end, currencies) =>\n `Coverage: ${start} → ${end} | Currencies: ${currencies}`,\n ratesGapsNone: \"Gaps: none\",\n ratesGaps: (list) => `Gaps: ${list}`,\n ratesUpdateFetching: \"Fetching ECB SDMX...\",\n ratesUpdateDone: (n) => `Done. Added ${n} new dates.`,\n ratesSnapshotWritten: (path) => `Snapshot written to ${path}`,\n ratesAutoUpdateStart: \"ECB rates are stale. Auto-updating…\",\n ratesAutoUpdateFailed: \"Auto-update failed. Proceeding with available rates.\",\n\n configLangSet: (lang) => `Language set to: ${lang}`,\n configCurrentLang: (lang) => `Current language: ${lang}`,\n\n classifyStarting: (n) => `Classifying ISINs... (${n} instruments)`,\n classifyDetected: (isin, product, bucket, rate) =>\n `Detected: ${product} (${isin}) → ${bucket} | ${rate}`,\n classifyConfirm: \"[Y/n/?]: \",\n classifyOverridePrompt: \"Instrument type:\",\n classifyDone: (confirmed, total) =>\n `Classification complete (${confirmed}/${total} confirmed).`,\n classifyWritten: (path) => `Written: ${path}`,\n classifyMergePrompt: (existing) =>\n `File already exists (${existing} confirmed ISINs). Add new ISINs? [Y/n]: `,\n classifyOfflineWarning: \"Offline mode: OpenFIGI lookup skipped.\",\n classifyUnknownType: (type) =>\n `Unrecognized type: ${type}. Please classify manually.`,\n classifyNonTtyError:\n \"Interactive mode requires a TTY. Use --offline to classify without a terminal.\",\n\n bucketAHeader: \"BUCKET A — CAPITAL INCOME (non-offsettable)\",\n bucketBHeader: \"BUCKET B — MISCELLANEOUS INCOME\",\n bucketAEtf: \"ETF (OICR)\",\n bucketABtpWl: \"Govt Bond WL\",\n bucketATotalTax: \"TOTAL TAX\",\n bucketBCarryApplied: (year) => `CARRY ${year}`,\n bucketBResult: \"RESULT\",\n bucketBCarryNote: \"(carriable over the next 4 years)\",\n warnMixedBuckets: \"Bucket B losses do not offset Bucket A plusvalenze.\",\n warnMixedAssets:\n \"WARNING: CSV contains mixed instrument types (e.g. ETFs + Stocks).\\n\" +\n \" Single-bucket calculation may not be fiscally correct.\\n\" +\n \" Run: minus-tracker classify trades.csv\",\n headerBucket: \"BUCKET\",\n warnUnclassifiedIsin: (isin) =>\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n carryForwardInvalidFormat:\n \"Invalid --carry-forward format. Use: YYYY:amount (e.g. 2023:2500)\",\n\n disclaimer: \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n\n // v0.7.0 dichiarazione section\n dichiarazioneHeader: (anno) => `── MODELLO REDDITI PF [TAX YEAR: ${anno}]`,\n dichiarazioneNota:\n \"Note: row codes are indicative — verify against the official tax form.\",\n dichiarazioneWarningRow:\n \"⚠ Row codes in square brackets are indicative. Check the current form version.\",\n quadroRTHeader: \"QUADRO RT — SECTION II (capital gains)\",\n quadroRTPlusvalenze: \"[RT-P] Total gains\",\n quadroRTMinusvalenze: \"[RT-M] Total losses\",\n quadroRTDifferenza: \"[RT-D] Difference\",\n quadroRTRiporto: (anno) => `[RT-C*] Prior loss ${anno} (applied)`,\n quadroRTImponibile: \"[RT-N] Net taxable\",\n quadroRTImposta: \"[RT-I] Tax (26%)\",\n quadroRTRiportabile: \"[RT-R] Losses to carry forward\",\n quadroRTPerdita: \"net loss:\",\n quadroRMHeader: \"QUADRO RM (capital income)\",\n quadroRMEtf26: \"[RM-A1] ETF/OICR gains (26%)\",\n quadroRMImposta: \"Substitute tax\",\n quadroRMBtp: \"[RM-A2] BTP/WL gains (12.5%)\",\n quadroRMDividendi: \"[RM-D] Foreign dividends (gross)\",\n quadroRMRitenuta: \"Foreign withholding (credit)\",\n quadroRMCedole: \"[RM-C] Bond coupons\",\n dichiarazioneDisclaimer:\n \"minus-tracker è un ausilio al calcolo, non consulenza fiscale.\",\n warnNoDichiarazioneSidecar:\n \"Tip: run 'minus-tracker classify <file>' first to enable Quadro RT/RM.\",\n warnNoCarryForwardProvided: \"(prior-year losses not applied)\",\n};\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { MinusTrackerConfig, SupportedLocale } from \"./types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport function getConfigPath(): string {\n if (process.platform === \"win32\") {\n const appData =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n return path.join(appData, \"minus-tracker\", \"config.json\");\n }\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n return path.join(xdg, \"minus-tracker\", \"config.json\");\n}\n\nexport function readConfig(): MinusTrackerConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), \"utf8\");\n return JSON.parse(raw) as MinusTrackerConfig;\n } catch {\n return {};\n }\n}\n\nexport function saveLocale(lang: SupportedLocale): void {\n const configPath = getConfigPath();\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n const existing = readConfig();\n const updated: MinusTrackerConfig = { ...existing, locale: lang };\n fs.writeFileSync(configPath, JSON.stringify(updated, null, 2) + \"\\n\", \"utf8\");\n}\n\nexport function resolveLocale(cliLang?: string): SupportedLocale {\n // 1. --lang flag\n if (cliLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(cliLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${cliLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return cliLang as SupportedLocale;\n }\n // 2. env var\n const envLang = process.env.MINUS_TRACKER_LANG;\n if (envLang !== undefined) {\n if (!SUPPORTED_LOCALES.includes(envLang as SupportedLocale)) {\n process.stderr.write(`Unsupported locale \"${envLang}\". Use: it, en\\n`);\n process.exit(2);\n }\n return envLang as SupportedLocale;\n }\n // 3. config file\n const config = readConfig();\n if (config.locale && SUPPORTED_LOCALES.includes(config.locale)) {\n return config.locale;\n }\n // 4. default\n return \"it\";\n}\n","import { it } from \"./it.js\";\nimport { en } from \"./en.js\";\nimport type { LocaleStrings, SupportedLocale } from \"./types.js\";\n\nexport { it, en };\nexport type { LocaleStrings, SupportedLocale };\nexport type { MinusTrackerConfig } from \"./types.js\";\nexport {\n resolveLocale,\n saveLocale,\n readConfig,\n getConfigPath,\n} from \"./settings.js\";\n\nexport function getStrings(locale: SupportedLocale): LocaleStrings {\n return locale === \"it\" ? it : en;\n}\n","export class ParseError extends Error {\n readonly code: \"INVALID_CSV\" | \"MISSING_COLUMN\";\n readonly columnName?: string;\n\n constructor(code: \"INVALID_CSV\");\n constructor(code: \"MISSING_COLUMN\", columnName: string);\n constructor(code: ParseError[\"code\"], columnName?: string) {\n const msg =\n code === \"INVALID_CSV\"\n ? \"Invalid CSV: unable to parse\"\n : `Missing required column: ${columnName}`;\n super(msg);\n this.name = \"ParseError\";\n this.code = code;\n this.columnName = columnName;\n }\n}\n\nexport class CalculationError extends Error {\n readonly isin: string;\n readonly date: string;\n\n constructor(isin: string, date: string) {\n super(`No open lots for ISIN ${isin} on ${date}`);\n this.name = \"CalculationError\";\n this.isin = isin;\n this.date = date;\n }\n}\n\nexport class ClassificationError extends Error {\n readonly code:\n | \"NETWORK_ERROR\"\n | \"SIDECAR_NOT_FOUND\"\n | \"SIDECAR_VERSION\"\n | \"SIDECAR_MALFORMED\"\n | \"WRITE_ERROR\";\n\n constructor(code: ClassificationError[\"code\"], message?: string) {\n super(\n message ??\n (code === \"SIDECAR_NOT_FOUND\"\n ? \"Sidecar file not found\"\n : code === \"SIDECAR_VERSION\"\n ? \"Sidecar version mismatch\"\n : code === \"SIDECAR_MALFORMED\"\n ? \"Sidecar file is malformed JSON\"\n : code === \"NETWORK_ERROR\"\n ? \"Network error contacting OpenFIGI\"\n : \"Failed to write sidecar file\"),\n );\n this.name = \"ClassificationError\";\n this.code = code;\n }\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Calculator } from \"../../calculator/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport { renderReport } from \"../renderer.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type {\n LotMethod,\n ClassificationMap,\n CarryForward,\n} from \"../../types.js\";\n\nexport async function runCalc(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\n \"Usage: minus-tracker calc [--method LIFO|FIFO] [--json] <file.csv>\\n\",\n );\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(s.errorCannotReadFile(filePath) + \"\\n\");\n return 1;\n }\n\n const method = (flags[\"method\"] as LotMethod) ?? \"LIFO\";\n if (method !== \"LIFO\" && method !== \"FIFO\") {\n stderr.write(\"--method must be LIFO or FIFO\\n\");\n return 2;\n }\n\n // Sidecar auto-discovery\n const sidecarPath = filePath.replace(/\\.csv$/i, \"\") + \".classify.json\";\n let classification: ClassificationMap | undefined;\n if (fs.existsSync(sidecarPath)) {\n try {\n const classifier = new Classifier({ interactive: false });\n classification = await classifier.load(sidecarPath);\n } catch {\n stderr.write(s.errorCannotLoadSidecar(sidecarPath) + \"\\n\");\n return 1;\n }\n }\n\n // --export-dichiarazione flag parsing (optional path value)\n const exportFlagRaw = flags[\"export-dichiarazione\"];\n const exportRequested =\n exportFlagRaw !== undefined && exportFlagRaw !== false;\n const exportPath =\n typeof exportFlagRaw === \"string\"\n ? exportFlagRaw\n : filePath.replace(/\\.csv$/i, \"\") + \".dichiarazione.json\";\n\n if (exportRequested && classification === undefined) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n return 1;\n }\n\n // --carry-forward flag parsing\n const rawCf: unknown = flags[\"carry-forward\"];\n const rawCarryForwards: string[] = Array.isArray(rawCf)\n ? (rawCf as string[])\n : rawCf\n ? [rawCf as string]\n : [];\n\n const cfRegex = /^\\d{4}:\\d+(\\.\\d+)?$/;\n for (const cf of rawCarryForwards) {\n if (!cfRegex.test(cf)) {\n stderr.write(s.carryForwardInvalidFormat + \"\\n\");\n return 2;\n }\n }\n\n // Load carry-forward config file\n const xdgConfig =\n process.env[\"XDG_CONFIG_HOME\"] ?? path.join(os.homedir(), \".config\");\n const cfConfigPath = path.join(\n xdgConfig,\n \"minus-tracker\",\n \"carryforward.json\",\n );\n const cfFromFile: Record<number, number> = {};\n if (fs.existsSync(cfConfigPath)) {\n try {\n const raw = JSON.parse(fs.readFileSync(cfConfigPath, \"utf-8\")) as {\n losses?: Array<{ year: number; amount: number }>;\n };\n for (const entry of raw.losses ?? []) {\n cfFromFile[entry.year] = entry.amount;\n }\n } catch {\n /* silently skip malformed config */\n }\n }\n\n // Merge: flag wins over config file\n const cfMerged: Record<number, number> = { ...cfFromFile };\n for (const cf of rawCarryForwards) {\n const [yearStr, amountStr] = cf.split(\":\");\n cfMerged[parseInt(yearStr, 10)] = parseFloat(amountStr);\n }\n const carryForward: CarryForward[] = Object.entries(cfMerged).map(\n ([year, amount]) => ({ year: parseInt(year, 10), amount }),\n );\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n const calculator = new Calculator(transactions, parser.warnings, {\n classification,\n carryForward: carryForward.length > 0 ? carryForward : undefined,\n incomeRows: parser.incomeRows,\n });\n const report = calculator.calculateGains(method);\n const carryForwardWasProvided = carryForward.length > 0;\n\n if (exportRequested) {\n try {\n await report.dichiarazione!.exportTo(exportPath);\n } catch {\n stderr.write(s.errorCannotWriteExport(exportPath) + \"\\n\");\n return 1;\n }\n }\n\n if (flags[\"json\"]) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { dichiarazione: _dichiarazione, ...jsonReport } = report;\n stdout.write(JSON.stringify(jsonReport, null, 2) + \"\\n\");\n } else {\n stdout.write(renderReport(report, s, carryForwardWasProvided) + \"\\n\");\n }\n\n if (exportRequested) {\n stdout.write(s.classifyWritten(exportPath) + \"\\n\");\n } else if (!report.dichiarazione) {\n stderr.write(s.warnNoDichiarazioneSidecar + \"\\n\");\n }\n\n return 0;\n}\n","import { fileURLToPath } from \"node:url\";\nimport * as path from \"node:path\";\nimport * as fs from \"node:fs\";\n\nexport type RatesSnapshot = Record<string, Record<string, number>>;\n\n// Load the bundled snapshot lazily; returns null if the file is absent (e.g. broken install)\nlet _bundled: RatesSnapshot | null | undefined = undefined;\n\nfunction getBundledSnapshot(): RatesSnapshot | null {\n if (_bundled !== undefined) return _bundled;\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const bundledPath = path.join(__dirname, \"../data/ecb-rates.json\");\n try {\n _bundled = JSON.parse(\n fs.readFileSync(bundledPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n _bundled = null;\n }\n return _bundled;\n}\n\nfunction getUserSnapshot(): RatesSnapshot | null {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ??\n path.join(process.env.USERPROFILE ?? \"~\", \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ??\n path.join(process.env.HOME ?? \"~\", \".config\");\n }\n const userPath = path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n try {\n return JSON.parse(fs.readFileSync(userPath, \"utf8\")) as RatesSnapshot;\n } catch {\n return null;\n }\n}\n\nexport function getActiveSnapshot(): RatesSnapshot {\n const bundled = getBundledSnapshot();\n const user = getUserSnapshot();\n if (!bundled && !user) {\n throw new Error(\n \"No ECB rates snapshot available. Run `minus-tracker rates --update` to fetch rates.\",\n );\n }\n if (!bundled) return user!;\n if (!user) return bundled;\n // Merge: user entries override bundled for same date keys\n const merged: RatesSnapshot = {};\n for (const ccy of new Set([...Object.keys(bundled), ...Object.keys(user)])) {\n merged[ccy] = { ...(bundled[ccy] ?? {}), ...(user[ccy] ?? {}) };\n }\n return merged;\n}\n\nexport function isSnapshotStale(\n snapshot: RatesSnapshot,\n today?: string,\n): boolean {\n const todayStr = today ?? new Date().toISOString().slice(0, 10);\n let maxDate = \"0000-01-01\";\n for (const dates of Object.values(snapshot)) {\n for (const d of Object.keys(dates)) {\n if (d > maxDate) maxDate = d;\n }\n }\n if (maxDate === \"0000-01-01\") return true;\n const diffDays =\n (new Date(todayStr).getTime() - new Date(maxDate).getTime()) /\n (1000 * 60 * 60 * 24);\n return diffDays >= 7;\n}\n\nfunction subtractDays(isoDate: string, days: number): string {\n const d = new Date(isoDate + \"T00:00:00Z\");\n d.setUTCDate(d.getUTCDate() - days);\n return d.toISOString().slice(0, 10);\n}\n\n/**\n * Look up ECB rate for a currency on a given date.\n * Returns 1.0 for EUR. Walks back up to 3 calendar days for weekend/holiday gaps.\n * Returns null if not found within the window.\n *\n * @param currency ISO 4217 currency code\n * @param date YYYY-MM-DD trade date\n * @param snapshot Optional override (used in tests to inject stub data)\n */\nexport function lookupRate(\n currency: string,\n date: string,\n snapshot?: RatesSnapshot,\n): number | null {\n if (currency === \"EUR\") return 1.0;\n\n const s = snapshot ?? getActiveSnapshot();\n const currencyRates = s[currency];\n if (!currencyRates) return null; // unsupported currency\n\n for (let i = 0; i <= 3; i++) {\n const d = i === 0 ? date : subtractDays(date, i);\n if (currencyRates[d] !== undefined) {\n return currencyRates[d];\n }\n }\n return null;\n}\n","export type WarningEntry =\n | { code: \"MISSING_ISIN\"; row: number }\n | { code: \"UNSUPPORTED_CURRENCY\"; row: number; currency: string }\n | { code: \"NO_ECB_RATE\"; row: number; currency: string; date: string }\n | { code: \"QUANTITY_ZERO\"; row: number }\n | { code: \"MISSING_ISIN_INCOME\"; row: number }\n | { code: \"ORPHAN_WITHHOLDING\"; isin: string; date: string };\n\nexport function warningToEnglish(w: WarningEntry): string {\n switch (w.code) {\n case \"MISSING_ISIN\":\n return `Row ${w.row}: missing ISIN — skipped`;\n case \"UNSUPPORTED_CURRENCY\":\n return `Row ${w.row}: unsupported currency ${w.currency} — skipped`;\n case \"NO_ECB_RATE\":\n return `Row ${w.row}: no ECB rate for ${w.currency} on ${w.date} — skipped`;\n case \"QUANTITY_ZERO\":\n return `Row ${w.row}: quantity is 0 — skipped`;\n case \"MISSING_ISIN_INCOME\":\n return `Row ${w.row}: blank ISIN on income row — skipped`;\n case \"ORPHAN_WITHHOLDING\":\n return `Withholding row for ${w.isin} on ${w.date}: no matching income row — skipped`;\n }\n}\n","import { Transaction, IncomeRow } from \"../types.js\";\nimport { ParseError } from \"../errors.js\";\nimport {\n lookupRate,\n getActiveSnapshot,\n RatesSnapshot,\n} from \"../rates/index.js\";\nimport { WarningEntry, warningToEnglish } from \"./warnings.js\";\n\nconst REQUIRED_COLUMNS = [\n \"ISIN\",\n \"Quantity\",\n \"Price\",\n \"Date\",\n \"Local value\",\n \"Local value currency\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Product\",\n] as const;\n\n// Income-row product keyword matching (case-insensitive substring match).\n// Tax keywords are checked first because \"DIVIDEND TAX\" also contains \"DIVIDEND\".\nconst INCOME_KEYWORDS = [\"DIVIDEND\", \"COUPON\", \"INTEREST\", \"CEDOLA\"] as const;\nconst TAX_KEYWORDS = [\"DIVIDEND TAX\", \"WITHHOLDING\", \"RITENUTA\"] as const;\n\ninterface IncomeCandidate {\n isin: string;\n product: string;\n date: string;\n incomeType: \"dividend\" | \"coupon\";\n localValue: number;\n currency: string;\n row: number;\n}\n\ninterface WithholdingCandidate {\n isin: string;\n date: string;\n amount: number; // absolute value, in source currency\n row: number;\n}\n\n/**\n * Parse a single CSV line, respecting RFC-4180-style double-quote escaping.\n * Does not support embedded newlines in fields (not needed for DEGIRO exports).\n */\nfunction parseCSVRow(line: string): string[] {\n const fields: string[] = [];\n let i = 0;\n\n while (i <= line.length) {\n // End of line — stop (handles trailing comma by not emitting extra empty field)\n if (i === line.length) break;\n\n if (line[i] === '\"') {\n // Quoted field\n let field = \"\";\n i++; // skip opening quote\n while (i < line.length) {\n if (line[i] === '\"' && i + 1 < line.length && line[i + 1] === '\"') {\n // Escaped double-quote\n field += '\"';\n i += 2;\n } else if (line[i] === '\"') {\n i++; // skip closing quote\n break;\n } else {\n field += line[i++];\n }\n }\n fields.push(field);\n if (i < line.length && line[i] === \",\") i++; // skip delimiter\n } else {\n // Unquoted field\n const start = i;\n while (i < line.length && line[i] !== \",\") i++;\n fields.push(line.slice(start, i));\n if (i < line.length) i++; // skip delimiter\n }\n }\n\n return fields;\n}\n\n/**\n * Parse a full CSV string (CRLF or LF line endings) into a 2-D array of strings.\n */\nfunction parseCSV(input: string): string[][] {\n const normalized = input.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n return normalized.split(\"\\n\").map(parseCSVRow);\n}\n\n/**\n * Convert a DEGIRO date \"DD-MM-YYYY\" to ISO format \"YYYY-MM-DD\".\n */\nfunction parseDate(ddmmyyyy: string): string {\n const [dd, mm, yyyy] = ddmmyyyy.split(\"-\");\n return `${yyyy}-${mm}-${dd}`;\n}\n\n/**\n * Parses a DEGIRO Transactions CSV export into normalised Transaction objects.\n *\n * Export from Activity → Transactions in the DEGIRO UI (not the Account Statement).\n * Supported currencies: EUR, USD, GBP, CHF. ECB historical rates are used for conversion.\n */\nexport class DEGIROParser {\n private _warningEntries: WarningEntry[] = [];\n private _snapshot?: RatesSnapshot;\n private _incomeRows: IncomeRow[] = [];\n\n constructor(snapshot?: RatesSnapshot) {\n this._snapshot = snapshot;\n }\n\n /**\n * Parse a DEGIRO Transactions CSV export string.\n *\n * @param csv - Raw UTF-8 string from the DEGIRO Transactions export.\n * @returns Array of normalised Transaction objects. Empty if no data rows are found.\n * @throws {ParseError} code `\"INVALID_CSV\"` — malformed CSV or binary content.\n * @throws {ParseError} code `\"MISSING_COLUMN\"` (+ `columnName`) — required column absent.\n *\n * Rows with missing ISIN, unsupported currency, zero quantity, or no ECB rate within\n * 3 trading days are skipped silently. Inspect `parser.warnings` for details.\n */\n parse(csv: string): Transaction[] {\n this._warningEntries = [];\n this._incomeRows = [];\n\n // Binary content (null bytes) is not valid CSV\n if (typeof csv !== \"string\" || csv.includes(\"\\x00\")) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n let rows: string[][];\n try {\n rows = parseCSV(csv);\n } catch {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n if (rows.length === 0) {\n throw new ParseError(\"INVALID_CSV\");\n }\n\n // Build column-name → index map from the header row\n const header = rows[0];\n const colIndex: Record<string, number> = {};\n for (let i = 0; i < header.length; i++) {\n colIndex[header[i].trim()] = i;\n }\n\n // Validate that every required column is present\n for (const col of REQUIRED_COLUMNS) {\n if (colIndex[col] === undefined) {\n throw new ParseError(\"MISSING_COLUMN\", col);\n }\n }\n\n // Resolve the rates snapshot once for the whole parse run\n const snapshot: RatesSnapshot = this._snapshot ?? getActiveSnapshot();\n\n const transactions: Transaction[] = [];\n const incomeCandidates: IncomeCandidate[] = [];\n const withholdingCandidates: WithholdingCandidate[] = [];\n\n for (let r = 1; r < rows.length; r++) {\n const row = rows[r];\n // 1-indexed row number: header = 1, first data row = 2\n const rowIndex = r + 1;\n\n // Skip completely empty rows (including the trailing empty line after a\n // final newline, and rows that are all whitespace)\n if (row.every((cell) => cell.trim() === \"\")) continue;\n\n const get = (col: string): string => (row[colIndex[col]] ?? \"\").trim();\n\n const isin = get(\"ISIN\");\n const product = get(\"Product\");\n const qtyStr = get(\"Quantity\");\n const rawQty = parseFloat(qtyStr);\n const qtyIsZeroOrBlank = qtyStr === \"\" || isNaN(rawQty) || rawQty === 0;\n\n // --- Income-row detection (Quantity == 0 or blank) ---\n if (qtyIsZeroOrBlank) {\n const upperProduct = product.toUpperCase();\n const isTaxKeyword = TAX_KEYWORDS.some((k) => upperProduct.includes(k));\n const isIncomeKeyword =\n !isTaxKeyword &&\n INCOME_KEYWORDS.some((k) => upperProduct.includes(k));\n\n if (isTaxKeyword || isIncomeKeyword) {\n const rawLocalValue = parseFloat(get(\"Local value\"));\n const localValue = isNaN(rawLocalValue) ? 0 : rawLocalValue;\n const isoDate = parseDate(get(\"Date\"));\n\n if (isTaxKeyword && localValue < 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n withholdingCandidates.push({\n isin,\n date: isoDate,\n amount: Math.abs(localValue),\n row: rowIndex,\n });\n continue;\n }\n\n if (isIncomeKeyword && localValue > 0) {\n if (!isin) {\n this._warningEntries.push({\n code: \"MISSING_ISIN_INCOME\",\n row: rowIndex,\n });\n continue;\n }\n const incomeType: \"dividend\" | \"coupon\" = upperProduct.includes(\n \"DIVIDEND\",\n )\n ? \"dividend\"\n : \"coupon\";\n incomeCandidates.push({\n isin,\n product,\n date: isoDate,\n incomeType,\n localValue,\n currency: get(\"Local value currency\"),\n row: rowIndex,\n });\n continue;\n }\n // Keyword matched but sign doesn't match the expected direction —\n // fall through to standard transaction handling below.\n }\n }\n\n // --- ISIN ---\n if (!isin) {\n this._warningEntries.push({ code: \"MISSING_ISIN\", row: rowIndex });\n continue;\n }\n\n // --- Quantity ---\n if (isNaN(rawQty) || rawQty === 0) {\n this._warningEntries.push({ code: \"QUANTITY_ZERO\", row: rowIndex });\n continue;\n }\n\n const type: \"BUY\" | \"SELL\" = rawQty > 0 ? \"BUY\" : \"SELL\";\n const quantity = Math.abs(rawQty);\n\n // --- Date ---\n const isoDate = parseDate(get(\"Date\"));\n\n // --- Currency & FX ---\n const currency = get(\"Local value currency\");\n const rawTotalLocal = parseFloat(get(\"Local value\"));\n const totalLocal = isNaN(rawTotalLocal) ? 0 : rawTotalLocal;\n\n let totalEUR: number;\n let fxRate: number | undefined;\n\n if (currency === \"EUR\") {\n totalEUR = Math.abs(totalLocal);\n fxRate = undefined;\n } else {\n const rate = lookupRate(currency, isoDate, snapshot);\n if (rate === null) {\n if (snapshot[currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: rowIndex,\n currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: rowIndex,\n currency,\n date: isoDate,\n });\n }\n continue;\n }\n totalEUR = Math.abs(totalLocal) / rate;\n fxRate = rate;\n }\n\n // --- Fees ---\n const rawFees = parseFloat(get(\"Transaction costs\"));\n const feesEUR = Math.abs(isNaN(rawFees) ? 0 : rawFees);\n\n // --- Remaining fields ---\n const pricePerUnit = parseFloat(get(\"Price\"));\n\n transactions.push({\n isin,\n product,\n date: isoDate,\n type,\n quantity,\n pricePerUnit,\n currency,\n totalLocal,\n totalEUR,\n feesEUR,\n fxRate,\n });\n }\n\n // --- Pair withholding candidates with income candidates by (ISIN, date) ---\n // Order-independent: candidates were collected regardless of CSV row order.\n const withholdingByKey = new Map<\n string,\n { isin: string; date: string; amount: number }\n >();\n for (const w of withholdingCandidates) {\n const key = `${w.isin}|${w.date}`;\n const existing = withholdingByKey.get(key);\n withholdingByKey.set(key, {\n isin: w.isin,\n date: w.date,\n amount: (existing?.amount ?? 0) + w.amount,\n });\n }\n const consumedKeys = new Set<string>();\n\n const incomeRows: IncomeRow[] = [];\n for (const c of incomeCandidates) {\n const key = `${c.isin}|${c.date}`;\n const matchedWithholding = withholdingByKey.get(key);\n\n let grossAmount: number;\n let withholdingTax: number;\n let fxRate: number | undefined;\n\n if (c.currency === \"EUR\") {\n grossAmount = Math.abs(c.localValue);\n withholdingTax = matchedWithholding?.amount ?? 0;\n fxRate = undefined;\n } else {\n const rate = lookupRate(c.currency, c.date, snapshot);\n if (rate === null) {\n if (snapshot[c.currency] === undefined) {\n this._warningEntries.push({\n code: \"UNSUPPORTED_CURRENCY\",\n row: c.row,\n currency: c.currency,\n });\n } else {\n this._warningEntries.push({\n code: \"NO_ECB_RATE\",\n row: c.row,\n currency: c.currency,\n date: c.date,\n });\n }\n continue;\n }\n grossAmount = Math.abs(c.localValue) / rate;\n withholdingTax = matchedWithholding\n ? matchedWithholding.amount / rate\n : 0;\n fxRate = rate;\n }\n\n if (matchedWithholding) consumedKeys.add(key);\n\n incomeRows.push({\n isin: c.isin,\n product: c.product,\n date: c.date,\n incomeType: c.incomeType,\n grossAmount,\n withholdingTax,\n currency: c.currency,\n fxRate,\n });\n }\n\n // Orphan withholding candidates — no matching income row for (ISIN, date)\n for (const [key, w] of withholdingByKey) {\n if (!consumedKeys.has(key)) {\n this._warningEntries.push({\n code: \"ORPHAN_WITHHOLDING\",\n isin: w.isin,\n date: w.date,\n });\n }\n }\n\n this._incomeRows = incomeRows;\n\n return transactions;\n }\n\n get warnings(): string[] {\n return this._warningEntries.map(warningToEnglish);\n }\n\n get warningEntries(): WarningEntry[] {\n return this._warningEntries;\n }\n\n get incomeRows(): IncomeRow[] {\n return this._incomeRows;\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport type {\n BucketAReport,\n BucketBReport,\n CarryForward,\n CarryForwardEntry,\n DichiarazioneReport,\n IncomeRow,\n QuadroRMReport,\n QuadroRTReport,\n} from \"../types.js\";\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nexport function buildQuadroRT(\n bucketB: BucketBReport,\n carryForward: CarryForward[],\n taxYear: number,\n): QuadroRTReport {\n const plusvalenze = bucketB.plusvalenze;\n const minusvalenze = bucketB.minusvalenze;\n const differenza = roundHalfUp(plusvalenze - minusvalenze);\n\n if (differenza > 0) {\n const sorted = [...carryForward].sort((a, b) => a.year - b.year);\n let remaining = differenza;\n const carryForwardApplied: CarryForwardEntry[] = [];\n\n for (const entry of sorted) {\n if (taxYear - entry.year > 4) continue;\n const consumed = Math.min(entry.amount, remaining);\n if (consumed > 0) {\n carryForwardApplied.push({\n annoOrigine: entry.year,\n importo: roundHalfUp(consumed),\n });\n remaining -= consumed;\n }\n }\n\n const imponibileNetto = roundHalfUp(remaining);\n const imposta = roundHalfUp(imponibileNetto * 0.26);\n\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied,\n imponibileNetto,\n imposta,\n carryForwardRiportato: [],\n };\n } else if (differenza < 0) {\n return {\n plusvalenze,\n minusvalenze,\n differenza,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [\n { annoOrigine: taxYear, importo: roundHalfUp(Math.abs(differenza)) },\n ],\n };\n } else {\n return {\n plusvalenze,\n minusvalenze,\n differenza: 0,\n carryForwardApplied: [],\n imponibileNetto: 0,\n imposta: 0,\n carryForwardRiportato: [],\n };\n }\n}\n\nexport function buildQuadroRM(\n bucketA: BucketAReport | undefined,\n incomeRows: IncomeRow[],\n taxYear: number,\n): QuadroRMReport {\n const groups = bucketA?.groups ?? [];\n\n const group26 = groups.find((g) => g.taxRate === 0.26);\n const group125 = groups.find((g) => g.taxRate === 0.125);\n\n const capitaleAliquota26 = {\n plusvalenze: group26?.plusvalenze ?? 0,\n imposta: group26?.imposta ?? 0,\n };\n const capitaleAliquota125 = {\n plusvalenze: group125?.plusvalenze ?? 0,\n imposta: group125?.imposta ?? 0,\n };\n\n const dividendiEsteri = incomeRows\n .filter((r) => r.incomeType === \"dividend\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n lordo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n const cedole = incomeRows\n .filter((r) => r.incomeType === \"coupon\")\n .map((r) => ({\n isin: r.isin,\n prodotto: r.product,\n importo: r.grossAmount,\n rittenutaEstera: r.withholdingTax,\n }));\n\n return {\n capitaleAliquota26,\n capitaleAliquota125,\n dividendiEsteri,\n cedole,\n };\n}\n\nexport function buildDichiarazioneReport(\n quadroRT: QuadroRTReport,\n quadroRM: QuadroRMReport,\n taxYear: number,\n): DichiarazioneReport {\n const version = 1;\n const modello = \"Redditi PF\" as const;\n const generatedAt = new Date().toISOString();\n\n return {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n exportTo: async (path: string) => {\n await writeFile(\n path,\n JSON.stringify(\n {\n version,\n annoImposta: taxYear,\n modello,\n generatedAt,\n quadroRT,\n quadroRM,\n },\n null,\n 2,\n ),\n );\n },\n };\n}\n","import type {\n Transaction,\n MatchedLot,\n GainsReport,\n LotMethod,\n CalculatorOptions,\n AssetClass,\n BucketAReport,\n BucketBReport,\n} from \"../types.js\";\nimport { CalculationError } from \"../errors.js\";\nimport {\n buildQuadroRT,\n buildQuadroRM,\n buildDichiarazioneReport,\n} from \"../dichiarazione/engine.js\";\n\ninterface Lot {\n date: string;\n quantity: number;\n pricePerUnitEUR: number;\n feesEUR: number;\n originalQty: number;\n fxRate?: number;\n}\n\nfunction roundHalfUp(x: number): number {\n return (Math.sign(x) * Math.round(Math.abs(x) * 100)) / 100;\n}\n\nfunction inferTaxYear(transactions: Transaction[]): {\n year: number;\n multipleYears: boolean;\n} {\n const counts: Record<string, number> = {};\n for (const t of transactions) {\n const y = t.date.slice(0, 4);\n counts[y] = (counts[y] ?? 0) + 1;\n }\n const years = Object.keys(counts);\n if (years.length === 0)\n return { year: new Date().getFullYear(), multipleYears: false };\n const year = parseInt(\n years.reduce((a, b) => (counts[a] >= counts[b] ? a : b)),\n );\n return { year, multipleYears: years.length > 1 };\n}\n\n/**\n * Computes Italian capital gains and losses from a list of parsed transactions.\n *\n * Uses LIFO or FIFO lot matching and converts all amounts to EUR via ECB rates.\n */\nexport class Calculator {\n private readonly _transactions: Transaction[];\n private readonly _parseWarnings: string[];\n private readonly _options: CalculatorOptions;\n\n constructor(\n transactions: Transaction[],\n parseWarnings?: string[],\n options?: CalculatorOptions,\n ) {\n this._transactions = transactions;\n this._parseWarnings = parseWarnings ?? [];\n this._options = options ?? {};\n }\n\n /**\n * Run LIFO or FIFO lot-matching over the transaction list.\n *\n * @param method - `\"LIFO\"` or `\"FIFO\"`.\n * @returns GainsReport with `plusvalenze`, `minusvalenze`, `netResult`, per-lot breakdown,\n * ECB rates used, and any accumulated warnings.\n * @throws {CalculationError} when a SELL has no matching open buy lots.\n * `error.isin` and `error.date` identify the problematic transaction.\n */\n calculateGains(method: LotMethod): GainsReport {\n const warnings: string[] = [...this._parseWarnings];\n\n // 1. Sort ascending by date; BUY before SELL on same date\n const sorted = [...this._transactions].sort((a, b) => {\n if (a.date < b.date) return -1;\n if (a.date > b.date) return 1;\n // Same date: BUY before SELL\n if (a.type === \"BUY\" && b.type === \"SELL\") return -1;\n if (a.type === \"SELL\" && b.type === \"BUY\") return 1;\n return 0;\n });\n\n // 2. Tax year inference\n const { year: taxYear, multipleYears } = inferTaxYear(sorted);\n if (multipleYears) {\n warnings.push(\n \"CSV contains transactions from multiple years — filter to a single year for accurate reporting.\",\n );\n }\n\n // 3. Lot matching\n const openLots = new Map<string, Lot[]>();\n const matchedLots: MatchedLot[] = [];\n const ratesUsed: Record<string, number> = {};\n\n for (const tx of sorted) {\n // Collect rates used\n if (tx.fxRate !== undefined) {\n ratesUsed[`${tx.currency}:${tx.date}`] = tx.fxRate;\n }\n\n if (tx.type === \"BUY\") {\n const lot: Lot = {\n date: tx.date,\n quantity: tx.quantity,\n pricePerUnitEUR: tx.totalEUR / tx.quantity,\n feesEUR: tx.feesEUR,\n originalQty: tx.quantity,\n fxRate: tx.fxRate,\n };\n if (!openLots.has(tx.isin)) openLots.set(tx.isin, []);\n openLots.get(tx.isin)!.push(lot);\n } else {\n // SELL\n const lots = openLots.get(tx.isin);\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const sellPricePerUnitEUR = tx.totalEUR / tx.quantity;\n let remainingSellQty = tx.quantity;\n\n while (remainingSellQty > 0) {\n if (!lots || lots.length === 0) {\n throw new CalculationError(tx.isin, tx.date);\n }\n\n const lot = method === \"LIFO\" ? lots[lots.length - 1] : lots[0];\n const matchedQty = Math.min(lot.quantity, remainingSellQty);\n\n // Fee allocation\n const allocatedBuyFeesEUR =\n lot.feesEUR * (matchedQty / lot.originalQty);\n const allocatedSellFeesEUR = tx.feesEUR * (matchedQty / tx.quantity);\n\n const buyCostEUR =\n lot.pricePerUnitEUR * matchedQty + allocatedBuyFeesEUR;\n const sellProceedsEUR =\n sellPricePerUnitEUR * matchedQty - allocatedSellFeesEUR;\n const gainLossEUR = sellProceedsEUR - buyCostEUR;\n\n matchedLots.push({\n isin: tx.isin,\n product: tx.product,\n quantity: matchedQty,\n buyDate: lot.date,\n sellDate: tx.date,\n buyPriceEUR: lot.pricePerUnitEUR,\n sellPriceEUR: sellPricePerUnitEUR,\n buyCostEUR: roundHalfUp(buyCostEUR),\n sellProceedsEUR: roundHalfUp(sellProceedsEUR),\n gainLossEUR: roundHalfUp(gainLossEUR),\n buyFxRate: lot.fxRate,\n sellFxRate: tx.fxRate,\n });\n\n lot.quantity -= matchedQty;\n remainingSellQty -= matchedQty;\n\n if (lot.quantity <= 0) {\n if (method === \"LIFO\") {\n lots.pop();\n } else {\n lots.shift();\n }\n }\n }\n }\n }\n\n // 4. Aggregate\n let plusvalenze = 0;\n let minusvalenze = 0;\n for (const lot of matchedLots) {\n if (lot.gainLossEUR > 0) plusvalenze += lot.gainLossEUR;\n else minusvalenze += Math.abs(lot.gainLossEUR);\n }\n\n // Two-bucket routing (only when classification map provided)\n if (this._options.classification) {\n const classification = this._options.classification;\n const unclassifiedIsins = new Set<string>();\n\n for (const lot of matchedLots) {\n const entry = classification[lot.isin];\n if (!entry) {\n lot.bucket = \"B\";\n unclassifiedIsins.add(lot.isin);\n } else if (entry.bucketGain === \"A\" && lot.gainLossEUR >= 0) {\n lot.bucket = \"A\";\n } else {\n lot.bucket = \"B\";\n }\n }\n\n for (const isin of unclassifiedIsins) {\n warnings.push(\n `ISIN ${isin} not found in classification map — assigned to Bucket B.`,\n );\n }\n\n // Bucket A computation\n const bucketALots = matchedLots.filter((l) => l.bucket === \"A\");\n const groupsByRate = new Map<\n number,\n { assetClasses: Set<string>; plusvalenze: number }\n >();\n for (const lot of bucketALots) {\n const entry = classification[lot.isin]!;\n const rate = entry.taxRate;\n if (!groupsByRate.has(rate))\n groupsByRate.set(rate, { assetClasses: new Set(), plusvalenze: 0 });\n const g = groupsByRate.get(rate)!;\n g.assetClasses.add(entry.assetClass);\n g.plusvalenze += lot.gainLossEUR;\n }\n const bucketAGroups = [...groupsByRate.entries()].map(([taxRate, g]) => ({\n taxRate,\n assetClasses: [...g.assetClasses] as AssetClass[],\n plusvalenze: roundHalfUp(g.plusvalenze),\n imposta: roundHalfUp(g.plusvalenze * taxRate),\n }));\n const bucketAReport: BucketAReport = {\n groups: bucketAGroups,\n totalImposta: roundHalfUp(\n bucketAGroups.reduce((s, g) => s + g.imposta, 0),\n ),\n };\n\n // Bucket B computation\n const bucketBLots = matchedLots.filter((l) => l.bucket === \"B\");\n let bPlusvalenze = 0;\n let bMinusvalenze = 0;\n for (const lot of bucketBLots) {\n if (lot.gainLossEUR >= 0) bPlusvalenze += lot.gainLossEUR;\n else bMinusvalenze += Math.abs(lot.gainLossEUR);\n }\n bPlusvalenze = roundHalfUp(bPlusvalenze);\n bMinusvalenze = roundHalfUp(bMinusvalenze);\n\n const carryForwards = [...(this._options.carryForward ?? [])].sort(\n (a, b) => a.year - b.year,\n );\n let remaining = bPlusvalenze - bMinusvalenze;\n let carryForwardApplied = 0;\n for (const entry of carryForwards) {\n if (taxYear - entry.year > 4) continue;\n if (remaining <= 0) break;\n const consumed = Math.min(entry.amount, remaining);\n carryForwardApplied += consumed;\n remaining -= consumed;\n }\n carryForwardApplied = roundHalfUp(carryForwardApplied);\n const bNetResult = roundHalfUp(\n bPlusvalenze - bMinusvalenze - carryForwardApplied,\n );\n const carryForwardRemaining = roundHalfUp(Math.max(0, -bNetResult));\n\n const bucketBReport: BucketBReport = {\n plusvalenze: bPlusvalenze,\n minusvalenze: bMinusvalenze,\n carryForwardApplied,\n carryForwardRemaining,\n netResult: bNetResult,\n };\n\n // Filter income rows to the tax year and build the dichiarazione report\n const allIncomeRows = this._options.incomeRows ?? [];\n const filteredIncomeRows = allIncomeRows.filter(\n (row) => new Date(row.date).getFullYear() === taxYear,\n );\n if (filteredIncomeRows.length < allIncomeRows.length) {\n warnings.push(`Income rows outside tax year ${taxYear} were skipped.`);\n }\n\n const quadroRT = buildQuadroRT(\n bucketBReport,\n this._options.carryForward ?? [],\n taxYear,\n );\n const quadroRM = buildQuadroRM(\n bucketAGroups.length > 0 ? bucketAReport : undefined,\n filteredIncomeRows,\n taxYear,\n );\n const dichiarazioneReport = buildDichiarazioneReport(\n quadroRT,\n quadroRM,\n taxYear,\n );\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n bucketA: bucketAGroups.length > 0 ? bucketAReport : undefined,\n bucketB: bucketBReport,\n dichiarazione: dichiarazioneReport,\n };\n } else {\n // Mixed-asset heuristic (no classification map)\n const allIsins = [...new Set(this._transactions.map((t) => t.isin))];\n const hasIePrefix = allIsins.some((isin) => isin.startsWith(\"IE\"));\n const hasOther = allIsins.some((isin) => !isin.startsWith(\"IE\"));\n if (hasIePrefix && hasOther) {\n warnings.push(\n \"AVVISO: CSV contiene tipi di strumenti misti (es. ETF + Azioni).\\n\" +\n \" Il calcolo a bucket unico può non essere fiscalmente corretto.\\n\" +\n \" Esegui: minus-tracker classify trades.csv\",\n );\n }\n }\n\n return {\n method,\n taxYear,\n plusvalenze: roundHalfUp(plusvalenze),\n minusvalenze: roundHalfUp(minusvalenze),\n netResult: roundHalfUp(plusvalenze - minusvalenze),\n lots: matchedLots,\n ratesUsed,\n warnings,\n generatedAt: new Date().toISOString(),\n };\n }\n}\n","import { ClassificationError } from \"../errors.js\";\nimport type {\n AssetClass,\n ClassificationEntry,\n ClassificationMap,\n ClassifyOptions,\n Transaction,\n} from \"../types.js\";\nimport * as fs from \"node:fs\";\nimport * as https from \"node:https\";\n\ntype HttpPost = (\n url: string,\n body: string,\n timeoutMs: number,\n) => Promise<{ status: number; data: string }>;\n\nfunction httpsPost(\n url: string,\n body: string,\n timeoutMs: number,\n): Promise<{ status: number; data: string }> {\n return new Promise((resolve, reject) => {\n const u = new URL(url);\n const req = https.request(\n {\n hostname: u.hostname,\n path: u.pathname + u.search,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => {\n data += chunk;\n });\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, data }));\n },\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy();\n reject(new Error(\"timeout\"));\n });\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\nconst GOVT_BOND_WHITELIST = new Set([\n \"IT\",\n \"DE\",\n \"FR\",\n \"AT\",\n \"BE\",\n \"NL\",\n \"ES\",\n \"PT\",\n \"FI\",\n \"IE\",\n \"LU\",\n \"GR\",\n \"SK\",\n \"SI\",\n \"LT\",\n \"LV\",\n \"EE\",\n \"MT\",\n \"CY\",\n \"US\",\n \"GB\",\n \"CH\",\n \"NO\",\n \"SE\",\n \"DK\",\n \"JP\",\n \"CA\",\n \"AU\",\n \"NZ\",\n \"SG\",\n \"HK\",\n \"KR\",\n]);\n\ntype TypeMapping = {\n assetClass: AssetClass;\n bucketGain: \"A\" | \"B\";\n bucketLoss: \"A\" | \"B\";\n taxRate: number;\n whiteListed: boolean | null;\n};\n\nconst SECURITY_TYPE_MAP: Record<string, TypeMapping> = {\n ETP: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Mutual Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Open-End Fund\": {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n \"Common Stock\": {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Equity: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Exchange Traded Commodity\": {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Corporate Bond\": {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Option: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Future: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Warrant: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CFD: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Swap: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Leverage Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Turbo Certificate\": {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n \"Capital Protected Certificate\": {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\nfunction isKnownType(t: string): boolean {\n return t === \"Government Bond\" || t in SECURITY_TYPE_MAP;\n}\n\nfunction classifyByType(\n isin: string,\n securityType: string,\n product: string,\n warnings: string[],\n): ClassificationEntry {\n if (securityType === \"Government Bond\") {\n const prefix = isin.slice(0, 2).toUpperCase();\n const whiteListed = GOVT_BOND_WHITELIST.has(prefix);\n return {\n product,\n assetClass: whiteListed ? \"GovtBondWL\" : \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: whiteListed ? 0.125 : 0.26,\n whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n const mapped = SECURITY_TYPE_MAP[securityType];\n if (mapped) {\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: false,\n source: \"openfigi\",\n };\n }\n\n // Unknown type — flag for manual review\n warnings.push(\n `Unrecognized type for ${isin}: ${securityType}. Please classify manually.`,\n );\n return {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n}\n\nconst ASSET_CLASS_DEFAULTS: Record<AssetClass, TypeMapping> = {\n ETF: {\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n Stock: {\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n ETC: {\n assetClass: \"ETC\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n GovtBondWL: {\n assetClass: \"GovtBondWL\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.125,\n whiteListed: true,\n },\n GovtBondOther: {\n assetClass: \"GovtBondOther\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: false,\n },\n CorpBond: {\n assetClass: \"CorpBond\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n Derivative: {\n assetClass: \"Derivative\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n LeverageCert: {\n assetClass: \"LeverageCert\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n },\n CapProtectedCert: {\n assetClass: \"CapProtectedCert\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n },\n};\n\n/**\n * Builds a user-confirmed `ClassificationEntry` from a caller-supplied\n * `AssetClass` override (v0.8.0 stateless mode). The caller picked the asset\n * class directly, so no ISIN-prefix whitelist lookup is performed here — the\n * caller owns gov-bond-whitelisted-vs-other correctness.\n */\nfunction buildEntryFromAssetClass(\n assetClass: AssetClass,\n product: string,\n): ClassificationEntry {\n const mapped = ASSET_CLASS_DEFAULTS[assetClass];\n return {\n product,\n assetClass: mapped.assetClass,\n bucketGain: mapped.bucketGain,\n bucketLoss: mapped.bucketLoss,\n taxRate: mapped.taxRate,\n whiteListed: mapped.whiteListed,\n confirmedByUser: true,\n source: \"user\",\n };\n}\n\ntype OpenFIGIItem = { securityType?: string; securityType2?: string };\ntype OpenFIGIResult = { data?: OpenFIGIItem[]; error?: string };\n\nexport class Classifier {\n private readonly interactive: boolean;\n\n constructor(options?: { interactive?: boolean }) {\n this.interactive = options?.interactive ?? true;\n }\n\n async load(sidecarPath: string): Promise<ClassificationMap> {\n if (!fs.existsSync(sidecarPath)) {\n throw new ClassificationError(\"SIDECAR_NOT_FOUND\");\n }\n\n let parsed: unknown;\n try {\n const raw = fs.readFileSync(sidecarPath, \"utf-8\");\n parsed = JSON.parse(raw);\n } catch {\n throw new ClassificationError(\"SIDECAR_MALFORMED\");\n }\n\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as Record<string, unknown>)[\"version\"] !== 1\n ) {\n throw new ClassificationError(\"SIDECAR_VERSION\");\n }\n\n return (parsed as { classifications: ClassificationMap }).classifications;\n }\n\n async classify(\n transactions: Transaction[],\n sidecarPath?: string,\n options?: ClassifyOptions,\n _httpPost: HttpPost = httpsPost,\n ): Promise<ClassificationMap> {\n // 1. Deduplicate ISINs; track first product name seen per ISIN\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n // 2. Build the \"already resolved\" set.\n // Sidecar mode (unchanged v0.6.0 behavior): only confirmedByUser:true\n // entries from disk are trusted; everything else is re-processed.\n // Stateless mode (sidecarPath undefined, v0.8.0): no disk access at\n // all — ALL entries in options.existingClassification are trusted\n // as-is, regardless of confirmedByUser.\n const confirmed: ClassificationMap = {};\n\n if (sidecarPath !== undefined) {\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await this.load(sidecarPath);\n for (const [isin, entry] of Object.entries(existingMap)) {\n if (entry.confirmedByUser) {\n confirmed[isin] = entry;\n }\n }\n }\n } else if (options?.existingClassification) {\n Object.assign(confirmed, options.existingClassification);\n }\n\n // overrides always win, in both modes, and are never sent to OpenFIGI\n for (const [isin, assetClass] of Object.entries(options?.overrides ?? {})) {\n const product = isinToProduct.get(isin) ?? isin;\n confirmed[isin] = buildEntryFromAssetClass(assetClass, product);\n }\n\n // 3. ISINs that need (re-)processing: not already resolved\n const toProcess: string[] = [];\n for (const isin of isinToProduct.keys()) {\n if (!(isin in confirmed)) {\n toProcess.push(isin);\n }\n }\n\n // 4. Batch-query OpenFIGI (skipped entirely in offline mode)\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n\n if (options?.offline) {\n for (const isin of toProcess) {\n const product = isinToProduct.get(isin) ?? isin;\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n }\n } else {\n const BATCH_SIZE = 10;\n const totalBatches = Math.ceil(toProcess.length / BATCH_SIZE);\n\n for (let i = 0; i < toProcess.length; i += BATCH_SIZE) {\n // Wait 6 s between consecutive batch requests\n if (i > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 6000));\n }\n\n const batch = toProcess.slice(i, i + BATCH_SIZE);\n const requestBody = JSON.stringify(\n batch.map((isin) => ({ idType: \"ID_ISIN\", idValue: isin })),\n );\n\n let response: { status: number; data: string } | null = null;\n\n // Try once; retry once on 5xx\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const r = await _httpPost(\n \"https://api.openfigi.com/v3/mapping\",\n requestBody,\n 10_000,\n );\n if (r.status < 500) {\n response = r;\n break;\n }\n // 5xx — loop will retry once\n } catch {\n // Network-level error — no retry\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n }\n\n if (response === null) {\n throw new ClassificationError(\"NETWORK_ERROR\");\n }\n\n const results = JSON.parse(response.data) as OpenFIGIResult[];\n\n for (let j = 0; j < batch.length; j++) {\n const isin = batch[j]!;\n const result = results[j];\n const product = isinToProduct.get(isin) ?? isin;\n\n if (\n !result ||\n result.error ||\n !result.data ||\n result.data.length === 0\n ) {\n warnings.push(\n `Unrecognized type for ${isin}: unknown. Please classify manually.`,\n );\n newEntries[isin] = {\n product,\n assetClass: \"Stock\",\n bucketGain: \"B\",\n bucketLoss: \"B\",\n taxRate: 0,\n whiteListed: null,\n confirmedByUser: false,\n source: \"user\",\n };\n continue;\n }\n\n const st = result.data[0]?.securityType;\n const st2 = result.data[0]?.securityType2;\n\n // Prefer the type that resolves to a known mapping\n const typeToUse =\n st && isKnownType(st)\n ? st\n : st2 && isKnownType(st2)\n ? st2\n : (st ?? st2 ?? \"Unknown\");\n\n newEntries[isin] = classifyByType(isin, typeToUse, product, warnings);\n }\n\n const batchIndex = i / BATCH_SIZE;\n options?.onBatchProgress?.(batchIndex + 1, totalBatches);\n }\n }\n\n // 5. Merge: confirmed/overrides entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar — only in sidecar mode; stateless mode never touches disk\n if (sidecarPath !== undefined) {\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications: mergedMap,\n warnings,\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n throw new ClassificationError(\"WRITE_ERROR\");\n }\n }\n\n return mergedMap;\n }\n}\n","import type { GainsReport, AssetClass } from \"../types.js\";\nimport type { LocaleStrings } from \"../i18n/types.js\";\n\nconst SEPARATOR = \"─\".repeat(72);\n\nfunction formatEUR(amount: number, locale: string): string {\n return new Intl.NumberFormat(locale, {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nfunction formatGainLoss(amount: number, locale: string): string {\n const formatted = formatEUR(Math.abs(amount), locale);\n return amount >= 0 ? `+${formatted}` : `-${formatted}`;\n}\n\nfunction assetClassLabel(ac: AssetClass, s: LocaleStrings): string {\n if (ac === \"ETF\") return s.bucketAEtf;\n if (ac === \"GovtBondWL\" || ac === \"GovtBondOther\") return s.bucketABtpWl;\n return ac;\n}\n\n/**\n * Renders the \"MODELLO REDDITI PF\" declaration section (Quadro RT + Quadro RM)\n * appended to the calc output when a classification sidecar is present.\n * See docs/prd/14-dichiarazione-engine.md \"CLI Output Section\".\n */\nfunction renderDichiarazione(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const d = report.dichiarazione!;\n const rt = d.quadroRT;\n const rm = d.quadroRM;\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n lines.push(SEPARATOR);\n lines.push(s.dichiarazioneHeader(d.annoImposta));\n lines.push(s.dichiarazioneNota);\n lines.push(\"\");\n\n // Quadro RT — Sezione II (redditi diversi)\n lines.push(s.quadroRTHeader);\n lines.push(` ${s.quadroRTPlusvalenze} ${fmt(rt.plusvalenze)}`);\n lines.push(` ${s.quadroRTMinusvalenze} ${fmt(rt.minusvalenze)}`);\n lines.push(` ${s.quadroRTDifferenza} ${fmt(rt.differenza)}`);\n for (const entry of rt.carryForwardApplied) {\n lines.push(\n ` ${s.quadroRTRiporto(entry.annoOrigine)} ${fmt(entry.importo)}`,\n );\n }\n let rtNLine = ` ${s.quadroRTImponibile} ${fmt(rt.imponibileNetto)}`;\n if (rt.differenza < 0) {\n rtNLine += ` (${s.quadroRTPerdita} ${fmt(Math.abs(rt.differenza))})`;\n }\n if (rt.differenza > 0 && !carryForwardWasProvided) {\n rtNLine += ` ${s.warnNoCarryForwardProvided}`;\n }\n lines.push(rtNLine);\n lines.push(` ${s.quadroRTImposta} ${fmt(rt.imposta)}`);\n if (rt.differenza < 0) {\n const riportato = rt.carryForwardRiportato.reduce(\n (sum, e) => sum + e.importo,\n 0,\n );\n lines.push(\n ` ${s.quadroRTRiportabile} ${fmt(riportato)} ${s.bucketBCarryNote}`,\n );\n }\n lines.push(\"\");\n\n // Quadro RM (redditi da capitale)\n lines.push(s.quadroRMHeader);\n if (rm.capitaleAliquota26.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMEtf26} ${fmt(rm.capitaleAliquota26.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A1] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota26.imposta)}`,\n );\n }\n if (rm.capitaleAliquota125.plusvalenze > 0) {\n lines.push(\n ` ${s.quadroRMBtp} ${fmt(rm.capitaleAliquota125.plusvalenze)}`,\n );\n lines.push(\n ` [RM-A2] ${s.quadroRMImposta} ${fmt(rm.capitaleAliquota125.imposta)}`,\n );\n }\n for (const div of rm.dividendiEsteri) {\n lines.push(\n ` ${s.quadroRMDividendi} — ${div.prodotto} (${div.isin}) ${fmt(div.lordo)}`,\n );\n lines.push(\n ` [RM-D] ${s.quadroRMRitenuta} — ${div.prodotto} (${div.isin}) ${fmt(div.rittenutaEstera)}`,\n );\n }\n for (const c of rm.cedole) {\n lines.push(\n ` ${s.quadroRMCedole} — ${c.prodotto} (${c.isin}) ${fmt(c.importo)}`,\n );\n }\n lines.push(\"\");\n\n lines.push(s.dichiarazioneWarningRow);\n lines.push(s.dichiarazioneDisclaimer);\n\n return lines.join(\"\\n\");\n}\n\nexport function renderReport(\n report: GainsReport,\n s: LocaleStrings,\n carryForwardWasProvided: boolean,\n): string {\n const fmt = (n: number) => formatEUR(n, s.numberLocale);\n const lines: string[] = [];\n\n const hasBuckets = !!(report.bucketA || report.bucketB);\n\n // Header\n lines.push(\n `${s.headerMethod}: ${report.method} | ${s.headerTaxYear}: ${report.taxYear}`,\n );\n lines.push(\"\");\n\n // Pre-table warnings (AVVISO: or WARNING:)\n for (const w of report.warnings) {\n if (w.startsWith(\"AVVISO:\") || w.startsWith(\"WARNING:\")) {\n lines.push(w);\n lines.push(\"\");\n }\n }\n\n // Column headers\n const headerCols = [\n s.headerIsin.padEnd(14),\n s.headerProduct.padEnd(20),\n s.headerQty.padStart(5),\n s.headerBuyDate.padEnd(12),\n s.headerSellDate.padEnd(12),\n s.headerBuyEur.padStart(14),\n s.headerSellEur.padStart(13),\n s.headerGainLoss.padStart(17),\n ];\n if (hasBuckets) {\n headerCols.push(s.headerBucket.padStart(8));\n }\n lines.push(headerCols.join(\" \"));\n\n // Lot rows\n for (const lot of report.lots) {\n const rowCols = [\n lot.isin.padEnd(14),\n lot.product.substring(0, 20).padEnd(20),\n String(lot.quantity).padStart(5),\n lot.buyDate.padEnd(12),\n lot.sellDate.padEnd(12),\n fmt(lot.buyCostEUR).padStart(14),\n fmt(lot.sellProceedsEUR).padStart(13),\n formatGainLoss(lot.gainLossEUR, s.numberLocale).padStart(17),\n ];\n if (hasBuckets) {\n rowCols.push((lot.bucket ?? \"\").padStart(8));\n }\n lines.push(rowCols.join(\" \"));\n }\n\n lines.push(\"\");\n lines.push(SEPARATOR);\n\n // Summary\n lines.push(`${s.summaryPlusvalenze}: ${fmt(report.plusvalenze)} EUR`);\n lines.push(`${s.summaryMinusvalenze}: ${fmt(report.minusvalenze)} EUR`);\n lines.push(`${s.summaryNetResult}: ${fmt(report.netResult)} EUR`);\n lines.push(\"\");\n lines.push(`${s.summaryWarnings}: ${report.warnings.length}`);\n lines.push(`${s.summaryGenerated}: ${report.generatedAt}`);\n lines.push(\"\");\n\n // Bucket A section\n if (report.bucketA && report.bucketA.groups.length > 0) {\n lines.push(SEPARATOR);\n lines.push(s.bucketAHeader);\n lines.push(SEPARATOR);\n for (const g of report.bucketA.groups) {\n const label = g.assetClasses\n .map((ac) => assetClassLabel(ac, s))\n .join(\", \");\n const rateDisplay = (g.taxRate * 100).toFixed(0);\n lines.push(\n `${label}: ${fmt(g.plusvalenze)} EUR → imposta: ${fmt(g.imposta)} EUR (${rateDisplay}%)`,\n );\n }\n lines.push(`${s.bucketATotalTax}: ${fmt(report.bucketA.totalImposta)} EUR`);\n lines.push(\"\");\n }\n\n // Bucket B section\n if (report.bucketB) {\n lines.push(SEPARATOR);\n lines.push(s.bucketBHeader);\n lines.push(SEPARATOR);\n lines.push(`PLUSVALENZE: ${fmt(report.bucketB.plusvalenze)} EUR`);\n lines.push(`MINUSVALENZE: ${fmt(report.bucketB.minusvalenze)} EUR`);\n if (report.bucketB.carryForwardApplied > 0) {\n lines.push(\n `${s.bucketBCarryApplied(0)}: ${fmt(report.bucketB.carryForwardApplied)} EUR`,\n );\n }\n let resultLine = `${s.bucketBResult}: ${fmt(report.bucketB.netResult)} EUR`;\n if (report.bucketB.carryForwardRemaining > 0) {\n resultLine += ` ${s.bucketBCarryNote}`;\n }\n lines.push(resultLine);\n lines.push(\"\");\n }\n\n // Mixed-buckets footnote\n if (hasBuckets) {\n lines.push(s.warnMixedBuckets);\n lines.push(\"\");\n }\n\n if (report.dichiarazione) {\n lines.push(renderDichiarazione(report, s, carryForwardWasProvided));\n } else {\n lines.push(s.disclaimer);\n }\n\n return lines.join(\"\\n\");\n}\n","import * as fs from \"node:fs\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport async function runValidate(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const filePath = positional[0];\n if (!filePath) {\n stderr.write(\"Usage: minus-tracker validate <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(filePath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${filePath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n stdout.write(s.validateOk(transactions.length, 0) + \"\\n\");\n\n for (const entry of parser.warningEntries) {\n let reason: string;\n switch (entry.code) {\n case \"MISSING_ISIN\":\n reason = s.warnMissingIsin(entry.row);\n break;\n case \"UNSUPPORTED_CURRENCY\":\n reason = s.warnUnsupportedCurrency(entry.row, entry.currency);\n break;\n case \"NO_ECB_RATE\":\n reason = s.warnNoEcbRate(entry.row, entry.currency, entry.date);\n break;\n case \"QUANTITY_ZERO\":\n reason = s.warnQuantityZero(entry.row);\n break;\n case \"MISSING_ISIN_INCOME\":\n reason = s.warnMissingIsinIncome(entry.row);\n break;\n case \"ORPHAN_WITHHOLDING\":\n reason = s.warnOrphanWithholding(entry.isin, entry.date);\n break;\n }\n stdout.write(reason + \"\\n\");\n }\n\n return 0;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport * as https from \"node:https\";\nimport { getActiveSnapshot, type RatesSnapshot } from \"../../rates/index.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\n\nexport function getSnapshotPath(): string {\n const platform = process.platform;\n let configDir: string;\n if (platform === \"win32\") {\n configDir =\n process.env.APPDATA ?? path.join(os.homedir(), \"AppData\", \"Roaming\");\n } else {\n configDir =\n process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\");\n }\n return path.join(configDir, \"minus-tracker\", \"ecb-rates.json\");\n}\n\nfunction countMissingBusinessDays(\n startDate: string,\n endDate: string,\n dates: Record<string, number>,\n): number {\n let missing = 0;\n const cursor = new Date(startDate + \"T00:00:00Z\");\n const end = new Date(endDate + \"T00:00:00Z\");\n while (cursor <= end) {\n const day = cursor.getUTCDay();\n const iso = cursor.toISOString().slice(0, 10);\n if (day !== 0 && day !== 6 && dates[iso] === undefined) missing++;\n cursor.setUTCDate(cursor.getUTCDate() + 1);\n }\n return missing;\n}\n\nfunction getCoverage(snapshot: RatesSnapshot): {\n start: string;\n end: string;\n currencies: string;\n gaps: string;\n} {\n let start = \"9999-12-31\";\n let end = \"0000-01-01\";\n const currencies: string[] = [];\n const gapEntries: string[] = [];\n\n for (const [ccy, dates] of Object.entries(snapshot)) {\n currencies.push(ccy);\n const keys = Object.keys(dates).sort();\n for (const d of keys) {\n if (d < start) start = d;\n if (d > end) end = d;\n }\n if (keys.length > 0) {\n const missing = countMissingBusinessDays(\n keys[0],\n keys[keys.length - 1],\n dates,\n );\n if (missing > 0) gapEntries.push(`${ccy}: ${missing}`);\n }\n }\n return {\n start,\n end,\n currencies: currencies.sort().join(\", \"),\n gaps: gapEntries.sort().join(\", \"),\n };\n}\n\nexport async function fetchEcbData(\n currency: string,\n): Promise<Record<string, number>> {\n return new Promise((resolve, reject) => {\n const url = `https://data-api.ecb.europa.eu/service/data/EXR/D.${currency}.EUR.SP00.A?format=csvdata&startPeriod=2019-01-01`;\n https\n .get(url, (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => (data += chunk));\n res.on(\"end\", () => {\n const rates: Record<string, number> = {};\n const lines = data.split(\"\\n\");\n // ECB SDMX csvdata format:\n // KEY,FREQ,CURRENCY,CURRENCY_DENOM,EXR_TYPE,EXR_SUFFIX,TIME_PERIOD,OBS_VALUE,...\n // DATE is column index 6, rate is column index 7\n for (const line of lines) {\n const parts = line.split(\",\");\n if (parts.length < 8) continue;\n const date = parts[6].trim();\n const rate = parseFloat(parts[7].trim());\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(date) && !isNaN(rate) && rate > 0) {\n rates[date] = rate;\n }\n }\n resolve(rates);\n });\n res.on(\"error\", reject);\n })\n .on(\"error\", reject);\n });\n}\n\nexport async function updateRates(\n snapshotPath: string,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n s: LocaleStrings,\n): Promise<void> {\n stdout.write(s.ratesUpdateFetching + \"\\n\");\n\n let existing: RatesSnapshot = {};\n try {\n existing = JSON.parse(\n fs.readFileSync(snapshotPath, \"utf8\"),\n ) as RatesSnapshot;\n } catch {\n /* file doesn't exist yet */\n }\n\n let addedCount = 0;\n for (const currency of [\"USD\", \"GBP\", \"CHF\"]) {\n try {\n const newRates = await fetchEcbData(currency);\n const existing_ccy = existing[currency] ?? {};\n let added = 0;\n for (const [date, rate] of Object.entries(newRates)) {\n if (!existing_ccy[date]) added++;\n existing_ccy[date] = rate;\n }\n existing[currency] = existing_ccy;\n addedCount += added;\n } catch {\n stderr.write(`Failed to fetch ${currency} rates\\n`);\n }\n }\n\n fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });\n fs.writeFileSync(\n snapshotPath,\n JSON.stringify(existing, null, 2) + \"\\n\",\n \"utf8\",\n );\n stdout.write(s.ratesUpdateDone(addedCount) + \"\\n\");\n stdout.write(s.ratesSnapshotWritten(snapshotPath) + \"\\n\");\n}\n\nexport async function runRates(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"check\"]) {\n const snapshot = getActiveSnapshot();\n const { start, end, currencies, gaps } = getCoverage(snapshot);\n stdout.write(s.ratesCoverage(start, end, currencies) + \"\\n\");\n stdout.write((gaps ? s.ratesGaps(gaps) : s.ratesGapsNone) + \"\\n\");\n return 0;\n }\n\n if (flags[\"update\"]) {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker rates --check | --update\\n\");\n return 2;\n}\n","import { resolveLocale, saveLocale } from \"../../i18n/settings.js\";\nimport type { LocaleStrings, SupportedLocale } from \"../../i18n/types.js\";\n\nconst SUPPORTED_LOCALES: SupportedLocale[] = [\"it\", \"en\"];\n\nexport async function runConfig(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n if (flags[\"lang\"] !== undefined) {\n const lang = flags[\"lang\"] as string;\n if (!SUPPORTED_LOCALES.includes(lang as SupportedLocale)) {\n stderr.write(`Unsupported locale \"${lang}\". Use: it, en\\n`);\n return 2;\n }\n saveLocale(lang as SupportedLocale);\n stdout.write(s.configLangSet(lang as SupportedLocale) + \"\\n\");\n return 0;\n }\n\n if (flags[\"show\"]) {\n const locale = resolveLocale();\n stdout.write(s.configCurrentLang(locale) + \"\\n\");\n return 0;\n }\n\n stderr.write(\"Usage: minus-tracker config --lang <it|en> | --show\\n\");\n return 2;\n}\n","/**\n * stress-test CLI command handler\n * Runs all (or a range of) stress test scenarios against the CLI binary.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSync } from \"node:child_process\";\nimport { generateCsv } from \"../../stress/generator.js\";\nimport { runScenario } from \"../../stress/runner.js\";\nimport { buildReport, formatTable, formatJson } from \"../../stress/reporter.js\";\nimport type { StressManifest } from \"../../stress/generator.js\";\n\nexport async function runStressTest(\n _positionals: string[],\n flags: Record<string, string | boolean>,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n // 1. Parse --range flag\n const rangeStr = (flags[\"range\"] as string | undefined) ?? \"1-100\";\n const rangeParts = rangeStr.split(\"-\");\n if (rangeParts.length !== 2) {\n stderr.write(\n `Error: --range must be in the form N-M (e.g. 1-100), got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n const rangeStart = parseInt(rangeParts[0], 10);\n const rangeEnd = parseInt(rangeParts[1], 10);\n if (\n isNaN(rangeStart) ||\n isNaN(rangeEnd) ||\n rangeStart <= 0 ||\n rangeEnd <= 0 ||\n rangeStart > rangeEnd ||\n rangeStart > 100 ||\n rangeEnd > 100\n ) {\n stderr.write(\n `Error: --range values must be positive integers between 1 and 100 with start ≤ end, got: \"${rangeStr}\"\\n`,\n );\n return 2;\n }\n\n // 2. Resolve manifest path\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const manifestPath = path.join(__dirname, \"../data/stress-manifest.json\");\n\n // 3. Load manifest\n let manifest: StressManifest;\n try {\n const raw = fs.readFileSync(manifestPath, \"utf8\");\n manifest = JSON.parse(raw) as StressManifest;\n } catch {\n stderr.write(`Error: could not load stress manifest at ${manifestPath}\\n`);\n return 1;\n }\n\n // 4. Resolve CLI binary\n const cliBin = process.argv[1];\n\n // 5. Create output directory\n let outputDir: string;\n const outputDirFlag = flags[\"output-dir\"] as string | undefined;\n if (outputDirFlag) {\n outputDir = outputDirFlag;\n fs.mkdirSync(outputDir, { recursive: true });\n } else {\n outputDir = fs.mkdtempSync(path.join(os.tmpdir(), \"minus-tracker-stress-\"));\n }\n\n // 8. Run global checks before scenarios\n const ratesCheck = spawnSync(\"node\", [cliBin, \"rates\", \"--check\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((ratesCheck.status ?? 1) !== 0) {\n stderr.write(\"Warning: rates --check failed; ECB rates may be stale.\\n\");\n }\n\n const configShow = spawnSync(\"node\", [cliBin, \"config\", \"--show\"], {\n encoding: \"utf8\",\n timeout: 15000,\n });\n if ((configShow.status ?? 1) !== 0) {\n stderr.write(\"Warning: config --show failed.\\n\");\n }\n\n // 6. Filter scenarios by range\n const scenarios = manifest.scenarios.filter((s) => {\n const id = parseInt(s.id, 10);\n return id >= rangeStart && id <= rangeEnd;\n });\n\n // 7. Run each scenario\n const results = [];\n for (const scenario of scenarios) {\n const csv = generateCsv(scenario);\n const result = runScenario(scenario, csv, outputDir, cliBin);\n results.push(result);\n stdout.write(\n ` [${result.pass ? \"✓\" : \"✗\"}] ${scenario.id} ${scenario.slug}\\n`,\n );\n }\n\n // 9. Build and output report\n const report = buildReport(results, rangeStart, rangeEnd);\n if (flags[\"json\"]) {\n stdout.write(formatJson(report) + \"\\n\");\n } else {\n stdout.write(formatTable(report) + \"\\n\");\n }\n\n // 10. Cleanup unless --keep\n if (!flags[\"keep\"]) {\n fs.rmSync(outputDir, { recursive: true, force: true });\n }\n\n // 11. Return exit code\n return report.failed === 0 ? 0 : 1;\n}\n","/**\n * DEGIRO CSV Generator for stress testing\n * Converts manifest scenarios into DEGIRO-format CSV strings.\n */\n\n/**\n * A single transaction from the manifest\n */\nexport interface ManifestTransaction {\n date: string; // YYYY-MM-DD\n product: string;\n isin: string;\n qty: number; // positive = BUY, negative = SELL\n price: number;\n currency: string; // EUR | USD | GBP | CHF\n fee: number; // absolute EUR fee\n}\n\n/**\n * Expected outcomes for a scenario\n */\nexport interface ManifestExpect {\n calc_exit: number;\n fifo_exit: number;\n json_exit: number;\n en_exit: number;\n validate_exit: number;\n warning_count: number;\n}\n\n/**\n * A single test scenario\n */\nexport interface ManifestScenario {\n id: string;\n category: string;\n slug: string;\n description: string;\n transactions: ManifestTransaction[];\n expect: ManifestExpect;\n special?: string;\n}\n\n/**\n * The full stress test manifest\n */\nexport interface StressManifest {\n version: string;\n ecb_reference_dates: Record<string, unknown>;\n scenarios: ManifestScenario[];\n}\n\n/**\n * Convert a date from YYYY-MM-DD to DD-MM-YYYY\n */\nfunction formatDate(isoDate: string): string {\n const [yyyy, mm, dd] = isoDate.split(\"-\");\n return `${dd}-${mm}-${yyyy}`;\n}\n\n/**\n * Generate a DEGIRO-format CSV string for a given scenario.\n *\n * DEGIRO CSV format (18 columns, exact order):\n * Date,Time,Product,ISIN,Exchange,Execution centre,Quantity,Price,Local value,\n * Local value currency,Value,Value currency,Exchange rate,Transaction costs,\n * Transaction costs currency,Total,Total currency,Order ID\n *\n * Returns the CSV content as a string (header + data rows, joined by \\n)\n */\nexport function generateCsv(scenario: ManifestScenario): string {\n // Handle special cases\n if (scenario.special === \"invalid_csv\") {\n // Return garbage bytes (invalid CSV)\n return \"NOT_A_CSV\\x00\\x01\\x02\\ngarbage data\";\n }\n\n // For normal and column-removal cases, build the header first\n const allColumns = [\n \"Date\",\n \"Time\",\n \"Product\",\n \"ISIN\",\n \"Exchange\",\n \"Execution centre\",\n \"Quantity\",\n \"Price\",\n \"Local value\",\n \"Local value currency\",\n \"Value\",\n \"Value currency\",\n \"Exchange rate\",\n \"Transaction costs\",\n \"Transaction costs currency\",\n \"Total\",\n \"Total currency\",\n \"Order ID\",\n ];\n\n // Remove columns based on special field\n let columns = allColumns;\n if (scenario.special === \"missing_col_isin\") {\n columns = columns.filter((c) => c !== \"ISIN\");\n } else if (scenario.special === \"missing_col_quantity\") {\n columns = columns.filter((c) => c !== \"Quantity\");\n } else if (scenario.special === \"missing_col_price\") {\n columns = columns.filter((c) => c !== \"Price\");\n }\n\n // Helper to build a CSV row, omitting columns as needed\n const buildRow = (\n values: Record<string, string>,\n includeColumns: string[],\n ): string => {\n return includeColumns.map((col) => values[col] ?? \"\").join(\",\");\n };\n\n // Build header\n const header = columns.join(\",\");\n const rows = [header];\n\n // Build data rows\n for (let rowIndex = 0; rowIndex < scenario.transactions.length; rowIndex++) {\n const tx = scenario.transactions[rowIndex];\n\n // Calculate local value: -qty * price\n // For BUY (qty > 0): localValue is negative (money out)\n // For SELL (qty < 0): localValue is positive (money in)\n const localValue = -tx.qty * tx.price;\n const total = localValue - tx.fee;\n\n const rowValues: Record<string, string> = {\n Date: formatDate(tx.date),\n Time: \"09:00\",\n Product: tx.product,\n ISIN: tx.isin,\n Exchange: \"XEUR\",\n \"Execution centre\": \"XEUR\",\n Quantity: String(tx.qty),\n Price: tx.price.toFixed(2),\n \"Local value\": localValue.toFixed(2),\n \"Local value currency\": tx.currency,\n Value: localValue.toFixed(2),\n \"Value currency\": \"EUR\",\n \"Exchange rate\": \"1\",\n \"Transaction costs\": (-Math.abs(tx.fee)).toFixed(2),\n \"Transaction costs currency\": \"EUR\",\n Total: total.toFixed(2),\n \"Total currency\": \"EUR\",\n \"Order ID\": `stress-${scenario.id}-${rowIndex}`,\n };\n\n const row = buildRow(rowValues, columns);\n rows.push(row);\n }\n\n return rows.join(\"\\n\");\n}\n","/**\n * CLI runner for minus-tracker stress test scenarios\n * Executes CLI commands against generated CSV files and collects results.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ManifestScenario } from \"./generator.js\";\n\n/**\n * Result of running a single CLI command\n */\nexport interface CommandResult {\n cmd: string;\n exitCode: number;\n stdout: string;\n stderr: string;\n pass: boolean;\n failure?: string;\n}\n\n/**\n * Result of running all commands for a scenario\n */\nexport interface ScenarioResult {\n id: string;\n category: string;\n slug: string;\n description: string;\n csvFile: string;\n results: CommandResult[];\n pass: boolean;\n warningCheckPass: boolean;\n expectedWarningCount: number;\n actualWarningCount: number;\n}\n\n/**\n * Extract the warning count from calc output.\n * The English renderer emits \"WARNINGS: N\" and the Italian renderer emits \"AVVERTENZE: N\".\n * We parse that numeric value directly rather than counting matching lines.\n */\nfunction countWarnings(text: string): number {\n if (!text) return 0;\n const match = text.match(/(?:warnings|avvertenze)\\s*:\\s*(\\d+)/i);\n return match ? parseInt(match[1], 10) : 0;\n}\n\n/**\n * Check if a string is valid JSON with required keys\n */\nfunction isValidJson(jsonStr: string): boolean {\n try {\n const obj = JSON.parse(jsonStr);\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"plusvalenze\" in obj &&\n \"minusvalenze\" in obj &&\n \"lots\" in obj\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Run a single CLI command and return its result\n */\nfunction runCommand(\n cliBin: string,\n args: string[],\n cmdLabel: string,\n expectedExitCode: number,\n outputShapeCheck: (\n stdout: string,\n stderr: string,\n exitCode: number,\n ) => { valid: boolean; reason?: string },\n): CommandResult {\n // Determine how to invoke the CLI\n const isJsFile = cliBin.endsWith(\".js\");\n let result;\n\n if (isJsFile) {\n // Run: node <cliBin> <args>\n result = spawnSync(\"node\", [cliBin, ...args], {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n } else {\n // Run: <cliBin> <args> (directly executable)\n result = spawnSync(cliBin, args, {\n encoding: \"utf8\",\n timeout: 15000,\n env: { ...process.env, MINUS_TRACKER_LANG: undefined },\n });\n }\n\n const exitCode = result.status ?? 1; // null status (timeout/error) → treat as 1\n const stdout = result.stdout || \"\";\n const stderr = result.stderr || \"\";\n\n // Check exit code match\n const exitCodeMatch = exitCode === expectedExitCode;\n\n // Check output shape\n const shapeCheck = outputShapeCheck(stdout, stderr, exitCode);\n\n let pass = exitCodeMatch && shapeCheck.valid;\n let failure: string | undefined;\n\n if (!exitCodeMatch) {\n failure = `exit code mismatch: expected ${expectedExitCode}, got ${exitCode}`;\n } else if (!shapeCheck.valid) {\n failure = shapeCheck.reason;\n }\n\n return {\n cmd: cmdLabel,\n exitCode,\n stdout,\n stderr,\n pass,\n failure,\n };\n}\n\n/**\n * Run all CLI commands for a scenario and return the result\n */\nexport function runScenario(\n scenario: ManifestScenario,\n csvContent: string,\n outputDir: string,\n cliBin: string,\n): ScenarioResult {\n // Write CSV to temp file\n const csvFileName = `${scenario.id}-${scenario.slug}.csv`;\n const csvFile = join(outputDir, csvFileName);\n writeFileSync(csvFile, csvContent, \"utf8\");\n\n const results: CommandResult[] = [];\n\n // Run calc command (LIFO)\n results.push(\n runCommand(\n cliBin,\n [\"calc\", csvFile],\n \"calc\",\n scenario.expect.calc_exit,\n (stdout) => {\n if (scenario.expect.calc_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --method FIFO\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--method\", \"FIFO\", csvFile],\n \"calc --method FIFO\",\n scenario.expect.fifo_exit,\n (stdout) => {\n if (scenario.expect.fifo_exit === 0) {\n const hasPlus = /plusvalenze|plusvalues/i.test(stdout);\n return hasPlus\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --method FIFO output missing plusvalenze/plusvalues (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --json\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--json\", csvFile],\n \"calc --json\",\n scenario.expect.json_exit,\n (stdout) => {\n if (scenario.expect.json_exit === 0) {\n const isValid = isValidJson(stdout);\n return isValid\n ? { valid: true }\n : {\n valid: false,\n reason:\n \"calc --json output is not valid JSON with required keys (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run calc --lang en\n results.push(\n runCommand(\n cliBin,\n [\"calc\", \"--lang\", \"en\", csvFile],\n \"calc --lang en\",\n scenario.expect.en_exit,\n (stdout) => {\n if (scenario.expect.en_exit === 0) {\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"calc --lang en output is empty (exit 0)\",\n };\n }\n return { valid: true };\n },\n ),\n );\n\n // Run validate\n results.push(\n runCommand(\n cliBin,\n [\"validate\", csvFile],\n \"validate\",\n scenario.expect.validate_exit,\n (stdout) => {\n if (scenario.expect.validate_exit !== 0) return { valid: true };\n const nonEmpty = stdout.trim().length > 0;\n return nonEmpty\n ? { valid: true }\n : {\n valid: false,\n reason: \"validate output is empty\",\n };\n },\n ),\n );\n\n // Count warnings using English locale calc output (immune to locale config)\n // English output emits \"WARNINGS: N\" which matches the /warn/i pattern reliably\n const enCalcResult = results[3]; // calc --lang en is always index 3\n const actualWarningCount = countWarnings(enCalcResult.stdout);\n const warningCheckPass = actualWarningCount >= scenario.expect.warning_count;\n\n // Determine overall pass\n const pass = results.every((r) => r.pass) && warningCheckPass;\n\n return {\n id: scenario.id,\n category: scenario.category,\n slug: scenario.slug,\n description: scenario.description,\n csvFile,\n results,\n pass,\n warningCheckPass,\n expectedWarningCount: scenario.expect.warning_count,\n actualWarningCount,\n };\n}\n","/**\n * Reporter for minus-tracker stress test suite.\n * Formats stress test results into human-readable table + summary.\n */\n\nimport type { ScenarioResult } from \"./runner.js\";\n\n/**\n * Aggregated stress test report\n */\nexport interface StressReport {\n totalScenarios: number;\n passed: number;\n failed: number;\n results: ScenarioResult[];\n rangeStart: number; // 1-based\n rangeEnd: number;\n}\n\n/**\n * Aggregates a list of ScenarioResult into a StressReport\n */\nexport function buildReport(\n results: ScenarioResult[],\n rangeStart: number,\n rangeEnd: number,\n): StressReport {\n const passed = results.filter((r) => r.pass).length;\n const failed = results.length - passed;\n\n return {\n totalScenarios: results.length,\n passed,\n failed,\n results,\n rangeStart,\n rangeEnd,\n };\n}\n\n/**\n * Format a ScenarioResult as a JSON string\n */\nexport function formatJson(report: StressReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Format a StressReport as a human-readable ASCII table\n */\nexport function formatTable(report: StressReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"STRESS TEST — minus-tracker\");\n lines.push(\n `Scenari: ${report.totalScenarios} | Passati: ${report.passed} | Falliti: ${report.failed}`,\n );\n lines.push(\"\");\n\n // Column header\n const idCol = \"ID\";\n const categoryCol = \"CATEGORIA\";\n const descCol = \"DESCRIZIONE\";\n const resultCol = \"ESITO\";\n\n lines.push(formatRow(idCol, categoryCol, descCol, resultCol));\n\n // Rows for each scenario\n for (const result of report.results) {\n const idStr = result.id.padStart(3, \"0\");\n const status = result.pass ? \"✓ PASS\" : \"✗ FAIL\";\n\n lines.push(formatRow(idStr, result.category, result.description, status));\n\n // If failed, add failure details\n if (!result.pass) {\n for (const cmdResult of result.results) {\n if (!cmdResult.pass) {\n if (cmdResult.failure) {\n let failureText = \"\";\n\n // Check for exit code mismatch pattern\n if (cmdResult.failure.includes(\"exit code mismatch\")) {\n const match = cmdResult.failure.match(\n /expected (\\d+), got (\\d+)/,\n );\n if (match) {\n failureText = `atteso exit ${match[1]}, ottenuto exit ${match[2]}`;\n }\n } else {\n // Output shape check failed\n failureText = `output shape non valido — ${cmdResult.failure}`;\n }\n\n lines.push(` → ${cmdResult.cmd}: ${failureText}`);\n }\n }\n }\n\n // Check warning count\n if (!result.warningCheckPass) {\n lines.push(\n ` → avvertenze: attese ≥${result.expectedWarningCount}, trovate ${result.actualWarningCount}`,\n );\n }\n }\n }\n\n // Summary footer\n lines.push(\"\");\n lines.push(`PASSATI: ${report.passed}`);\n lines.push(`FALLITI: ${report.failed}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single table row with fixed column widths\n */\nfunction formatRow(\n id: string,\n category: string,\n description: string,\n esito: string,\n): string {\n // Column widths: ID=4, CATEGORIA=22, DESCRIZIONE=40, ESITO=8\n const idWidth = 4;\n const categoryWidth = 22;\n const descWidth = 40;\n const esitoWidth = 8;\n\n // Pad/truncate columns\n const idCol = id.padEnd(idWidth);\n const categoryCol = truncateOrPad(category, categoryWidth);\n const descCol = truncateOrPad(description, descWidth);\n const esitoCol = esito.padEnd(esitoWidth);\n\n return `${idCol} ${categoryCol} ${descCol} ${esitoCol}`;\n}\n\n/**\n * Truncate a string to max width with \"..\" suffix, or pad to width\n */\nfunction truncateOrPad(str: string, maxWidth: number): string {\n if (str.length > maxWidth) {\n return str.slice(0, maxWidth - 2) + \"..\";\n }\n return str.padEnd(maxWidth);\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DEGIROParser } from \"../../parser/index.js\";\nimport { Classifier } from \"../../classifier/index.js\";\nimport { ParseError } from \"../../errors.js\";\nimport type { LocaleStrings } from \"../../i18n/types.js\";\nimport type { ClassificationMap } from \"../../types.js\";\n\nexport async function runClassify(\n positional: string[],\n flags: Record<string, string | boolean>,\n s: LocaleStrings,\n stdout: NodeJS.WritableStream,\n stderr: NodeJS.WritableStream,\n): Promise<number> {\n const offline = Boolean(flags[\"offline\"]);\n\n // TTY check — FIRST, before any file I/O\n if (!process.stdin.isTTY && !offline) {\n stderr.write(s.classifyNonTtyError + \"\\n\");\n return 2;\n }\n\n const csvPath = positional[0];\n if (!csvPath) {\n stderr.write(\"Usage: minus-tracker classify [--offline] <file.csv>\\n\");\n return 2;\n }\n\n let csv: string;\n try {\n csv = fs.readFileSync(csvPath, \"utf8\");\n } catch {\n stderr.write(`Cannot read file: ${csvPath}\\n`);\n return 1;\n }\n\n const parser = new DEGIROParser();\n let transactions;\n try {\n transactions = parser.parse(csv);\n } catch (err) {\n if (err instanceof ParseError) {\n if (err.code === \"INVALID_CSV\") {\n stderr.write(s.errorInvalidCsv + \"\\n\");\n } else {\n stderr.write(s.errorMissingColumn(err.columnName!) + \"\\n\");\n }\n return 1;\n }\n throw err;\n }\n\n // Derive sidecar path: replace .csv extension with .classify.json\n const base = csvPath.replace(/\\.csv$/i, \"\");\n const sidecarPath = base + \".classify.json\";\n\n if (offline) {\n // Offline mode: skip OpenFIGI; write stub user-confirmed entries for each unique ISIN\n stdout.write(s.classifyOfflineWarning + \"\\n\");\n\n const isinToProduct = new Map<string, string>();\n for (const tx of transactions) {\n if (tx.isin && !isinToProduct.has(tx.isin)) {\n isinToProduct.set(tx.isin, tx.product);\n }\n }\n\n const classifications: ClassificationMap = {};\n for (const [isin, product] of isinToProduct.entries()) {\n classifications[isin] = {\n product,\n assetClass: \"ETF\",\n bucketGain: \"A\",\n bucketLoss: \"B\",\n taxRate: 0.26,\n whiteListed: null,\n confirmedByUser: true,\n source: \"user\",\n };\n }\n\n const sidecarContent = JSON.stringify(\n {\n version: 1,\n generatedAt: new Date().toISOString(),\n classifications,\n warnings: [],\n },\n null,\n 2,\n );\n\n try {\n fs.writeFileSync(sidecarPath, sidecarContent, \"utf-8\");\n } catch {\n stderr.write(`Cannot write sidecar: ${sidecarPath}\\n`);\n return 1;\n }\n\n stdout.write(s.classifyWritten(sidecarPath) + \"\\n\");\n return 0;\n }\n\n // Online interactive mode\n const classifier = new Classifier({ interactive: true });\n\n if (fs.existsSync(sidecarPath)) {\n const existingMap = await classifier.load(sidecarPath);\n const confirmedCount = Object.values(existingMap).filter(\n (e) => e.confirmedByUser,\n ).length;\n stdout.write(s.classifyMergePrompt(confirmedCount));\n\n const answer = await readLine(process.stdin);\n if (answer.trim().toLowerCase() === \"n\") {\n return 0;\n }\n }\n\n await classifier.classify(transactions, sidecarPath);\n stdout.write(s.classifyWritten(path.resolve(sidecarPath)) + \"\\n\");\n return 0;\n}\n\nfunction readLine(stream: NodeJS.ReadableStream): Promise<string> {\n return new Promise((resolve) => {\n let buf = \"\";\n const onData = (chunk: Buffer | string) => {\n buf += chunk.toString();\n const nl = buf.indexOf(\"\\n\");\n if (nl !== -1) {\n stream.removeListener(\"data\", onData);\n resolve(buf.slice(0, nl));\n }\n };\n stream.on(\"data\", onData);\n });\n}\n"],"mappings":"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","names":["path","path","fs","path","os","path","fs","get","isoDate","path","roundHalfUp","fs","resolve","fs","fs","path","os","https","resolve","SUPPORTED_LOCALES","fs","path","os","fileURLToPath","spawnSync","writeFileSync","join","fileURLToPath","spawnSync","fs","path","resolve"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gabrielerandelli/minus-tracker",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Italian capital-gains/loss calculator for
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"description": "Italian capital-gains/loss calculator (LIFO/FIFO) with CLI and MCP server for AI agents — Regime Dichiarativo",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -52,7 +52,14 @@
|
|
|
52
52
|
"csv",
|
|
53
53
|
"lifo",
|
|
54
54
|
"fifo",
|
|
55
|
-
"typescript"
|
|
55
|
+
"typescript",
|
|
56
|
+
"mcp",
|
|
57
|
+
"mcp-server",
|
|
58
|
+
"model-context-protocol",
|
|
59
|
+
"ai-agent",
|
|
60
|
+
"llm-tools",
|
|
61
|
+
"claude",
|
|
62
|
+
"tax-calculator"
|
|
56
63
|
],
|
|
57
64
|
"homepage": "https://github.com/gabrielerandelli/minus-tracker#readme",
|
|
58
65
|
"bugs": {
|