@gabrielerandelli/minus-tracker 0.6.0 → 0.7.0
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/dist/cli/index.js +572 -171
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +295 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -1
- package/dist/index.d.ts +68 -1
- package/dist/index.js +295 -21
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
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/calculator/index.ts","../../src/classifier/index.ts","../../src/cli/renderer.ts","../../src/cli/commands/rates.ts","../../src/cli/commands/validate.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\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","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\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","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 { getActiveSnapshot, isSnapshotStale } from \"../../rates/index.js\";\nimport { updateRates, getSnapshotPath } from \"./rates.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 try {\n const snapshot = getActiveSnapshot();\n if (isSnapshotStale(snapshot)) {\n stderr.write(s.ratesAutoUpdateStart + \"\\n\");\n try {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n } catch {\n stderr.write(s.ratesAutoUpdateFailed + \"\\n\");\n }\n }\n } catch {\n // bundled snapshot missing and no user snapshot — updateRates will handle it\n stderr.write(s.ratesAutoUpdateStart + \"\\n\");\n try {\n await updateRates(getSnapshotPath(), stdout, stderr, s);\n } catch {\n stderr.write(s.ratesAutoUpdateFailed + \"\\n\");\n }\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 // --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 });\n const report = calculator.calculateGains(method);\n\n if (flags[\"json\"]) {\n stdout.write(JSON.stringify(report, null, 2) + \"\\n\");\n } else {\n stdout.write(renderReport(report, s) + \"\\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\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 }\n}\n","import { Transaction } 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/**\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\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\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\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 // --- ISIN ---\n const isin = get(\"ISIN\");\n if (!isin) {\n this._warningEntries.push({ code: \"MISSING_ISIN\", row: rowIndex });\n continue;\n }\n\n // --- Quantity ---\n const rawQty = parseFloat(get(\"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 const product = get(\"Product\");\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 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","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\";\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 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 };\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 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: ${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\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 _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. Load existing sidecar; separate confirmed from to-process\n const confirmed: ClassificationMap = {};\n\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\n // 3. ISINs that need (re-)processing: not user-confirmed\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\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n const BATCH_SIZE = 10;\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: 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\n // 5. Merge: confirmed entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar\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 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\nexport function renderReport(report: GainsReport, s: LocaleStrings): 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 lines.push(s.disclaimer);\n\n return lines.join(\"\\n\");\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 * 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 }\n stdout.write(reason + \"\\n\");\n }\n\n return 0;\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","fs","resolve","fs","path","os","https","resolve","fs","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 },\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 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: ${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\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 _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. Load existing sidecar; separate confirmed from to-process\n const confirmed: ClassificationMap = {};\n\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\n // 3. ISINs that need (re-)processing: not user-confirmed\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\n const warnings: string[] = [];\n const newEntries: ClassificationMap = {};\n const BATCH_SIZE = 10;\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: 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\n // 5. Merge: confirmed entries take precedence and remain untouched\n const mergedMap: ClassificationMap = { ...newEntries, ...confirmed };\n\n // 6. Write sidecar\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 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"]}
|