@jaskrowo/xtb2sure 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,39 +15,37 @@ Convert XTB `.xlsx` exports into Sure-compatible CSV files.
15
15
 
16
16
  ## Install
17
17
 
18
+ Install globally with npm:
19
+
18
20
  ```bash
19
- npm install
21
+ npm install -g @jaskrowo/xtb2sure
20
22
  ```
21
23
 
24
+ This makes the `xtb2sure` command available everywhere on your system.
25
+
22
26
  ## Usage
23
27
 
24
28
  ```bash
25
- node ./src/cli.ts convert <input.xlsx> --currency <CODE> --account "<ACCOUNT_NAME>"
29
+ xtb2sure convert <input.xlsx> --currency <CODE> --account "<ACCOUNT_NAME>"
26
30
  ```
27
31
 
28
32
  Options:
29
33
 
30
- - `--out-dir <dir>`
31
- - `--transactions-out <file>`
32
- - `--investments-out <file>`
33
- - `--strict`
34
- - `--dry-run`
35
- - `--report <file>`
34
+ - `--out-dir <dir>` — output directory for default file names (defaults to the input file's directory)
35
+ - `--transactions-out <file>` — transactions CSV path
36
+ - `--investments-out <file>` — investments CSV path
37
+ - `--strict` — fail on the first conversion issue
38
+ - `--dry-run` — do not write output files
39
+ - `--report <file>` — write conversion report JSON to file
36
40
 
37
41
  Examples:
38
42
 
39
43
  ```bash
40
- node ./src/cli.ts convert sample_data/IKE/input.xlsx --currency USD --account "XTB IKE"
44
+ xtb2sure convert report.xlsx --currency USD --account "XTB IKE"
41
45
  ```
42
46
 
43
47
  ```bash
44
- node ./src/cli.ts convert sample_data/PLN/input.xlsx --currency PLN --account "XTB PLN" --out-dir ./out
45
-
46
- Or via npm script:
47
-
48
- ```bash
49
- npm run convert -- sample_data/PLN/input.xlsx --currency PLN --account "XTB PLN"
50
- ```
48
+ xtb2sure convert report.xlsx --currency PLN --account "XTB PLN" --out-dir ./out
51
49
  ```
52
50
 
53
51
  ## Exit codes
@@ -58,6 +56,12 @@ npm run convert -- sample_data/PLN/input.xlsx --currency PLN --account "XTB PLN"
58
56
 
59
57
  ## Development
60
58
 
59
+ Build the compiled output:
60
+
61
+ ```bash
62
+ npm run build
63
+ ```
64
+
61
65
  Run tests (Node.js test runner):
62
66
 
63
67
  ```bash
package/bin/xtb2sure.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import "../src/cli.ts";
3
+ import "../dist/cli.js";
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { Command, CommanderError } from "commander";
4
+ import { convertFile } from "./index.js";
5
+ const EXIT_SUCCESS = 0;
6
+ const EXIT_CONVERSION_FAILURE = 1;
7
+ const EXIT_USAGE_ERROR = 2;
8
+ async function main(argv) {
9
+ let exitCode = EXIT_SUCCESS;
10
+ const program = new Command();
11
+ program
12
+ .name("xtb2sure")
13
+ .description("Convert XTB .xlsx exports into Sure-compatible CSV files")
14
+ .showHelpAfterError()
15
+ .exitOverride();
16
+ program
17
+ .command("convert")
18
+ .argument("<input.xlsx>", "Path to XTB export workbook")
19
+ .requiredOption("--currency <code>", "Account currency for all investment rows")
20
+ .requiredOption("--account <name>", "Account label used in generated CSV files")
21
+ .option("--out-dir <dir>", "Output directory for default file names")
22
+ .option("--transactions-out <file>", "Transactions CSV path")
23
+ .option("--investments-out <file>", "Investments CSV path")
24
+ .option("--strict", "Fail on first conversion issue", false)
25
+ .option("--dry-run", "Do not write output files", false)
26
+ .option("--report <file>", "Write conversion report JSON to file")
27
+ .action(async (inputPath, options) => {
28
+ try {
29
+ const result = await convertFile({
30
+ account: options.account,
31
+ currency: options.currency,
32
+ dryRun: options.dryRun,
33
+ inputPath,
34
+ investmentsOut: options.investmentsOut,
35
+ outDir: options.outDir,
36
+ report: options.report,
37
+ strict: options.strict,
38
+ transactionsOut: options.transactionsOut,
39
+ });
40
+ const totals = result.reportData.totals;
41
+ console.error(`Converted rows=${totals.sourceRows}, transactions=${totals.transactionRows}, investments=${totals.investmentRows}, ignored=${totals.ignoredRows}, issues=${totals.issues}`);
42
+ if (options.dryRun) {
43
+ console.error("Dry run enabled: files were not written.");
44
+ }
45
+ else {
46
+ console.error(`Transactions CSV: ${result.transactionsOutPath}`);
47
+ console.error(`Investments CSV: ${result.investmentsOutPath}`);
48
+ }
49
+ if (totals.issues > 0 && options.strict) {
50
+ exitCode = EXIT_CONVERSION_FAILURE;
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.error(error instanceof Error ? error.message : String(error));
55
+ exitCode = EXIT_CONVERSION_FAILURE;
56
+ }
57
+ });
58
+ try {
59
+ await program.parseAsync(argv, { from: "user" });
60
+ return exitCode;
61
+ }
62
+ catch (error) {
63
+ if (error instanceof CommanderError) {
64
+ if (error.code === "commander.helpDisplayed") {
65
+ return EXIT_SUCCESS;
66
+ }
67
+ return EXIT_USAGE_ERROR;
68
+ }
69
+ console.error(error instanceof Error ? error.message : String(error));
70
+ return EXIT_CONVERSION_FAILURE;
71
+ }
72
+ }
73
+ main(process.argv.slice(2))
74
+ .then((code) => {
75
+ process.exitCode = code;
76
+ })
77
+ .catch((error) => {
78
+ console.error(error instanceof Error ? error.message : String(error));
79
+ process.exitCode = EXIT_CONVERSION_FAILURE;
80
+ });
81
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAa3B,KAAK,UAAU,IAAI,CAAC,IAAc;IAC9B,IAAI,QAAQ,GAAG,YAAY,CAAC;IAE5B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,OAAO;SACF,IAAI,CAAC,UAAU,CAAC;SAChB,WAAW,CAAC,0DAA0D,CAAC;SACvE,kBAAkB,EAAE;SACpB,YAAY,EAAE,CAAC;IAEpB,OAAO;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,QAAQ,CAAC,cAAc,EAAE,6BAA6B,CAAC;SACvD,cAAc,CAAC,mBAAmB,EAAE,0CAA0C,CAAC;SAC/E,cAAc,CAAC,kBAAkB,EAAE,2CAA2C,CAAC;SAC/E,MAAM,CAAC,iBAAiB,EAAE,yCAAyC,CAAC;SACpE,MAAM,CAAC,2BAA2B,EAAE,uBAAuB,CAAC;SAC5D,MAAM,CAAC,0BAA0B,EAAE,sBAAsB,CAAC;SAC1D,MAAM,CAAC,UAAU,EAAE,gCAAgC,EAAE,KAAK,CAAC;SAC3D,MAAM,CAAC,WAAW,EAAE,2BAA2B,EAAE,KAAK,CAAC;SACvD,MAAM,CAAC,iBAAiB,EAAE,sCAAsC,CAAC;SACjE,MAAM,CAAC,KAAK,EAAE,SAAiB,EAAE,OAA8B,EAAE,EAAE;QAChE,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;gBAC7B,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS;gBACT,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,eAAe,EAAE,OAAO,CAAC,eAAe;aAC3C,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YACxC,OAAO,CAAC,KAAK,CACT,kBAAkB,MAAM,CAAC,UAAU,kBAAkB,MAAM,CAAC,eAAe,iBAAiB,MAAM,CAAC,cAAc,aAAa,MAAM,CAAC,WAAW,YAAY,MAAM,CAAC,MAAM,EAAE,CAC9K,CAAC;YAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,KAAK,CAAC,qBAAqB,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;gBACjE,OAAO,CAAC,KAAK,CAAC,oBAAoB,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACtC,QAAQ,GAAG,uBAAuB,CAAC;YACvC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtE,QAAQ,GAAG,uBAAuB,CAAC;QACvC,CAAC;IACL,CAAC,CAAC,CAAC;IAEP,IAAI,CAAC;QACD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAEjD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBAC3C,OAAO,YAAY,CAAC;YACxB,CAAC;YAED,OAAO,gBAAgB,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,OAAO,uBAAuB,CAAC;IACnC,CAAC;AACL,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACtB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;IACX,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC5B,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACb,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,QAAQ,GAAG,uBAAuB,CAAC;AAC/C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { CashOperationRow, ConversionResult } from "./types.ts";
2
+ export interface ConvertOptions {
3
+ account: string;
4
+ currency: string;
5
+ strict: boolean;
6
+ }
7
+ export declare function convertRows(rows: CashOperationRow[], options: ConvertOptions): ConversionResult;
8
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAuC,MAAM,YAAY,CAAC;AAE1G,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,gBAAgB,CAqE/F"}
@@ -1,41 +1,22 @@
1
- import { excelSerialToYYYYMMDD } from "./date.ts";
2
- import {
3
- buildInvestmentName,
4
- buildTransactionNotes,
5
- classifyOperation,
6
- formatQtyOutput,
7
- parseTradeValues,
8
- } from "./operations.ts";
9
- import type { CashOperationRow, ConversionResult, InvestmentRecord, TransactionRecord } from "./types.ts";
10
-
11
- export interface ConvertOptions {
12
- account: string;
13
- currency: string;
14
- strict: boolean;
15
- }
16
-
17
- export function convertRows(rows: CashOperationRow[], options: ConvertOptions): ConversionResult {
18
- const transactions: TransactionRecord[] = [];
19
- const investments: InvestmentRecord[] = [];
20
- const issues: ConversionResult["issues"] = [];
1
+ import { excelSerialToYYYYMMDD } from "./date.js";
2
+ import { buildInvestmentName, buildTransactionNotes, classifyOperation, formatQtyOutput, parseTradeValues, } from "./operations.js";
3
+ export function convertRows(rows, options) {
4
+ const transactions = [];
5
+ const investments = [];
6
+ const issues = [];
21
7
  let ignoredRows = 0;
22
-
23
8
  for (const [index, row] of rows.entries()) {
24
9
  const typeClass = classifyOperation(row.Type);
25
-
26
10
  if (row.Type === "Total") {
27
11
  ignoredRows += 1;
28
12
  continue;
29
13
  }
30
-
31
14
  if (typeClass === "ignored") {
32
15
  ignoredRows += 1;
33
16
  continue;
34
17
  }
35
-
36
18
  try {
37
19
  const date = excelSerialToYYYYMMDD(row.Time);
38
-
39
20
  if (typeClass === "transaction") {
40
21
  transactions.push({
41
22
  account: options.account,
@@ -44,12 +25,12 @@ export function convertRows(rows: CashOperationRow[], options: ConvertOptions):
44
25
  name: row.Type,
45
26
  notes: buildTransactionNotes(row),
46
27
  });
47
- } else {
28
+ }
29
+ else {
48
30
  const { qty, qtyHadExplicitDecimal } = parseTradeValues(row.Comment);
49
31
  const signedQtyValue = row.Type === "Stock sell" ? forceNegative(qty) : forcePositive(qty);
50
32
  const signedQty = formatQtyOutput(signedQtyValue, row.Instrument, qtyHadExplicitDecimal, row.Product);
51
33
  const price = calculateUnitPriceFromAmount(row.Amount, signedQtyValue);
52
-
53
34
  investments.push({
54
35
  account: options.account,
55
36
  currency: options.currency,
@@ -60,23 +41,21 @@ export function convertRows(rows: CashOperationRow[], options: ConvertOptions):
60
41
  ticker: row.Ticker,
61
42
  });
62
43
  }
63
- } catch (error) {
44
+ }
45
+ catch (error) {
64
46
  issues.push({
65
47
  message: error instanceof Error ? error.message : String(error),
66
48
  operationId: row.ID,
67
49
  rowIndex: index + 2,
68
50
  });
69
-
70
51
  if (options.strict) {
71
52
  break;
72
53
  }
73
54
  }
74
55
  }
75
-
76
56
  if (rows.some((row) => row.Product.trim() !== "" && row.Product.trim() !== "IKE")) {
77
57
  investments.sort((a, b) => a.date.localeCompare(b.date));
78
58
  }
79
-
80
59
  return {
81
60
  ignoredRows,
82
61
  investments,
@@ -84,27 +63,23 @@ export function convertRows(rows: CashOperationRow[], options: ConvertOptions):
84
63
  transactions,
85
64
  };
86
65
  }
87
-
88
- function normalizeAmount(amount: number): string {
66
+ function normalizeAmount(amount) {
89
67
  return amount.toFixed(2);
90
68
  }
91
-
92
- function forceNegative(qty: string): string {
69
+ function forceNegative(qty) {
93
70
  const num = Math.abs(Number(qty));
94
71
  return String(-num);
95
72
  }
96
-
97
- function forcePositive(qty: string): string {
73
+ function forcePositive(qty) {
98
74
  const num = Math.abs(Number(qty));
99
75
  return String(num);
100
76
  }
101
-
102
- function calculateUnitPriceFromAmount(amount: number, qty: string): string {
77
+ function calculateUnitPriceFromAmount(amount, qty) {
103
78
  const qtyNumber = Math.abs(Number(qty));
104
79
  if (!Number.isFinite(amount) || !Number.isFinite(qtyNumber) || qtyNumber === 0) {
105
80
  throw new Error(`Cannot calculate price from amount=${amount} and qty=${qty}`);
106
81
  }
107
-
108
82
  const price = Math.abs(amount) / qtyNumber;
109
83
  return String(Number(price.toFixed(8)));
110
84
  }
85
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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"}
package/dist/csv.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function toCsv(headers: string[], rows: string[][]): string;
2
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAEA,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAEjE"}
@@ -1,5 +1,5 @@
1
1
  import { stringify } from "csv-stringify/sync";
2
-
3
- export function toCsv(headers: string[], rows: string[][]): string {
2
+ export function toCsv(headers, rows) {
4
3
  return stringify([headers, ...rows]);
5
4
  }
5
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,KAAK,CAAC,OAAiB,EAAE,IAAgB;IACrD,OAAO,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC"}
package/dist/date.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function excelSerialToYYYYMMDD(serial: number): string;
2
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAmBA,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5D"}
@@ -1,22 +1,19 @@
1
1
  const EXCEL_EPOCH_UTC_MS = Date.UTC(1899, 11, 30);
2
2
  const DAY_MS = 24 * 60 * 60 * 1000;
3
-
4
- function excelSerialToDate(serial: number): Date {
3
+ function excelSerialToDate(serial) {
5
4
  if (!Number.isFinite(serial)) {
6
5
  throw new Error(`Invalid Excel serial date: ${serial}`);
7
6
  }
8
-
9
7
  const utcMs = EXCEL_EPOCH_UTC_MS + Math.round(serial * DAY_MS);
10
8
  return new Date(utcMs);
11
9
  }
12
-
13
- function formatDateYYYYMMDD(date: Date): string {
10
+ function formatDateYYYYMMDD(date) {
14
11
  const year = date.getUTCFullYear();
15
12
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
16
13
  const day = String(date.getUTCDate()).padStart(2, "0");
17
14
  return `${year}-${month}-${day}`;
18
15
  }
19
-
20
- export function excelSerialToYYYYMMDD(serial: number): string {
16
+ export function excelSerialToYYYYMMDD(serial) {
21
17
  return formatDateYYYYMMDD(excelSerialToDate(serial));
22
18
  }
19
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.js","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAClD,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnC,SAAS,iBAAiB,CAAC,MAAc;IACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAC/D,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAChD,OAAO,kBAAkB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CashOperationRow } from "./types.ts";
2
+ export declare function readCashOperationsSheet(inputPath: string): CashOperationRow[];
3
+ //# sourceMappingURL=excel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excel.d.ts","sourceRoot":"","sources":["../src/excel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgBnD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAqC7E"}
@@ -1,8 +1,5 @@
1
1
  import xlsx from "xlsx";
2
- import type { CashOperationRow } from "./types.ts";
3
-
4
2
  const { readFile, utils } = xlsx;
5
-
6
3
  const REQUIRED_COLUMNS = [
7
4
  "Type",
8
5
  "Instrument",
@@ -13,67 +10,53 @@ const REQUIRED_COLUMNS = [
13
10
  "Comment",
14
11
  "Product",
15
12
  "Position ID",
16
- ] as const;
17
-
18
- export function readCashOperationsSheet(inputPath: string): CashOperationRow[] {
13
+ ];
14
+ export function readCashOperationsSheet(inputPath) {
19
15
  const workbook = withSuppressedXlsxWarnings(() => readFile(inputPath, { cellDates: false, raw: true }));
20
16
  const worksheet = workbook.Sheets["Cash Operations"];
21
-
22
17
  if (!worksheet) {
23
18
  throw new Error("Input workbook is missing required sheet: Cash Operations");
24
19
  }
25
-
26
- const matrix = utils.sheet_to_json<unknown[]>(worksheet, {
20
+ const matrix = utils.sheet_to_json(worksheet, {
27
21
  header: 1,
28
22
  defval: "",
29
23
  raw: true,
30
24
  });
31
-
32
- const headerIndex = matrix.findIndex(
33
- (row) => Array.isArray(row) && row[0] === "Type" && row.includes("Time") && row.includes("Comment"),
34
- );
35
-
25
+ const headerIndex = matrix.findIndex((row) => Array.isArray(row) && row[0] === "Type" && row.includes("Time") && row.includes("Comment"));
36
26
  if (headerIndex < 0) {
37
27
  throw new Error("Could not locate Cash Operations header row");
38
28
  }
39
-
40
- const headers = matrix[headerIndex] as unknown[];
41
- const headerToIndex = new Map<string, number>();
29
+ const headers = matrix[headerIndex];
30
+ const headerToIndex = new Map();
42
31
  headers.forEach((header, index) => {
43
32
  headerToIndex.set(String(header), index);
44
33
  });
45
-
46
34
  const missingColumns = REQUIRED_COLUMNS.filter((column) => !headerToIndex.has(column));
47
35
  if (missingColumns.length > 0) {
48
36
  throw new Error(`Missing required columns in Cash Operations: ${missingColumns.join(", ")}`);
49
37
  }
50
-
51
38
  const bodyRows = matrix
52
39
  .slice(headerIndex + 1)
53
40
  .filter((row) => Array.isArray(row) && String(row[0]).trim().length > 0);
54
41
  return bodyRows.map((row) => normalizeRowFromArray(row, headerToIndex));
55
42
  }
56
-
57
- function withSuppressedXlsxWarnings<T>(fn: () => T): T {
43
+ function withSuppressedXlsxWarnings(fn) {
58
44
  const originalConsoleError = console.error;
59
- console.error = (...args: unknown[]) => {
45
+ console.error = (...args) => {
60
46
  if (typeof args[0] === "string" && args[0].includes("Bad uncompressed size:")) {
61
47
  return;
62
48
  }
63
-
64
49
  originalConsoleError(...args);
65
50
  };
66
-
67
51
  try {
68
52
  return fn();
69
- } finally {
53
+ }
54
+ finally {
70
55
  console.error = originalConsoleError;
71
56
  }
72
57
  }
73
-
74
- function normalizeRowFromArray(row: unknown[], headerToIndex: Map<string, number>): CashOperationRow {
75
- const get = (column: string): unknown => row[headerToIndex.get(column) ?? -1];
76
-
58
+ function normalizeRowFromArray(row, headerToIndex) {
59
+ const get = (column) => row[headerToIndex.get(column) ?? -1];
77
60
  return {
78
61
  Type: stringValue(get("Type")),
79
62
  Instrument: stringValue(get("Instrument")),
@@ -86,20 +69,17 @@ function normalizeRowFromArray(row: unknown[], headerToIndex: Map<string, number
86
69
  "Position ID": stringValue(get("Position ID")),
87
70
  };
88
71
  }
89
-
90
- function stringValue(value: unknown): string {
72
+ function stringValue(value) {
91
73
  if (value === null || value === undefined) {
92
74
  return "";
93
75
  }
94
-
95
76
  return String(value);
96
77
  }
97
-
98
- function numberValue(value: unknown): number {
78
+ function numberValue(value) {
99
79
  const num = typeof value === "number" ? value : Number(value);
100
80
  if (!Number.isFinite(num)) {
101
81
  return Number.NaN;
102
82
  }
103
-
104
83
  return num;
105
84
  }
85
+ //# sourceMappingURL=excel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excel.js","sourceRoot":"","sources":["../src/excel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export interface ConvertFileOptions {
2
+ inputPath: string;
3
+ currency: string;
4
+ account: string;
5
+ outDir?: string;
6
+ transactionsOut?: string;
7
+ investmentsOut?: string;
8
+ strict?: boolean;
9
+ dryRun?: boolean;
10
+ report?: string;
11
+ }
12
+ export interface ConvertFileResult {
13
+ transactionsCsv: string;
14
+ investmentsCsv: string;
15
+ transactionsOutPath: string;
16
+ investmentsOutPath: string;
17
+ reportData: {
18
+ inputPath: string;
19
+ totals: {
20
+ sourceRows: number;
21
+ transactionRows: number;
22
+ investmentRows: number;
23
+ ignoredRows: number;
24
+ issues: number;
25
+ };
26
+ issues: Array<{
27
+ rowIndex: number;
28
+ operationId?: string;
29
+ message: string;
30
+ }>;
31
+ };
32
+ }
33
+ export declare function convertFile(options: ConvertFileOptions): Promise<ConvertFileResult>;
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE;YACJ,UAAU,EAAE,MAAM,CAAC;YACnB,eAAe,EAAE,MAAM,CAAC;YACxB,cAAc,EAAE,MAAM,CAAC;YACvB,WAAW,EAAE,MAAM,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,MAAM,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC9E,CAAC;CACL;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwDzF"}
@@ -1,68 +1,29 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { convertRows } from "./convert.ts";
4
- import { readCashOperationsSheet } from "./excel.ts";
5
- import { investmentsToCsv, transactionsToCsv } from "./output.ts";
6
-
7
- export interface ConvertFileOptions {
8
- inputPath: string;
9
- currency: string;
10
- account: string;
11
- outDir?: string;
12
- transactionsOut?: string;
13
- investmentsOut?: string;
14
- strict?: boolean;
15
- dryRun?: boolean;
16
- report?: string;
17
- }
18
-
19
- export interface ConvertFileResult {
20
- transactionsCsv: string;
21
- investmentsCsv: string;
22
- transactionsOutPath: string;
23
- investmentsOutPath: string;
24
- reportData: {
25
- inputPath: string;
26
- totals: {
27
- sourceRows: number;
28
- transactionRows: number;
29
- investmentRows: number;
30
- ignoredRows: number;
31
- issues: number;
32
- };
33
- issues: Array<{ rowIndex: number; operationId?: string; message: string }>;
34
- };
35
- }
36
-
37
- export async function convertFile(options: ConvertFileOptions): Promise<ConvertFileResult> {
3
+ import { convertRows } from "./convert.js";
4
+ import { readCashOperationsSheet } from "./excel.js";
5
+ import { investmentsToCsv, transactionsToCsv } from "./output.js";
6
+ export async function convertFile(options) {
38
7
  const rows = readCashOperationsSheet(options.inputPath);
39
8
  const result = convertRows(rows, {
40
9
  account: options.account,
41
10
  currency: options.currency,
42
11
  strict: Boolean(options.strict),
43
12
  });
44
-
45
13
  const transactionsCsv = transactionsToCsv(result.transactions);
46
14
  const investmentsCsv = investmentsToCsv(result.investments);
47
-
48
15
  const resolvedOutDir = options.outDir
49
16
  ? path.resolve(options.outDir)
50
17
  : path.dirname(path.resolve(options.inputPath));
51
18
  const baseName = path.basename(options.inputPath, path.extname(options.inputPath));
52
- const transactionsOutPath = path.resolve(
53
- options.transactionsOut ?? path.join(resolvedOutDir, `${baseName}.transactions.csv`),
54
- );
55
- const investmentsOutPath = path.resolve(
56
- options.investmentsOut ?? path.join(resolvedOutDir, `${baseName}.investments.csv`),
57
- );
58
-
19
+ const transactionsOutPath = path.resolve(options.transactionsOut ?? path.join(resolvedOutDir, `${baseName}.transactions.csv`));
20
+ const investmentsOutPath = path.resolve(options.investmentsOut ?? path.join(resolvedOutDir, `${baseName}.investments.csv`));
59
21
  if (!options.dryRun) {
60
22
  await mkdir(path.dirname(transactionsOutPath), { recursive: true });
61
23
  await mkdir(path.dirname(investmentsOutPath), { recursive: true });
62
24
  await writeFile(transactionsOutPath, transactionsCsv, "utf8");
63
25
  await writeFile(investmentsOutPath, investmentsCsv, "utf8");
64
26
  }
65
-
66
27
  const reportData = {
67
28
  inputPath: path.resolve(options.inputPath),
68
29
  totals: {
@@ -74,7 +35,6 @@ export async function convertFile(options: ConvertFileOptions): Promise<ConvertF
74
35
  },
75
36
  issues: result.issues,
76
37
  };
77
-
78
38
  if (options.report) {
79
39
  const reportPath = path.resolve(options.report);
80
40
  if (!options.dryRun) {
@@ -82,7 +42,6 @@ export async function convertFile(options: ConvertFileOptions): Promise<ConvertF
82
42
  await writeFile(reportPath, `${JSON.stringify(reportData, null, 2)}\n`, "utf8");
83
43
  }
84
44
  }
85
-
86
45
  return {
87
46
  transactionsCsv,
88
47
  investmentsCsv,
@@ -91,3 +50,4 @@ export async function convertFile(options: ConvertFileOptions): Promise<ConvertF
91
50
  reportData,
92
51
  };
93
52
  }
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CashOperationRow } from "./types.ts";
2
+ export declare function classifyOperation(type: string): "transaction" | "investment" | "ignored";
3
+ export interface ParsedTradeValues {
4
+ price: string;
5
+ qty: string;
6
+ qtyHadExplicitDecimal: boolean;
7
+ }
8
+ export declare function parseTradeValues(comment: string): ParsedTradeValues;
9
+ export declare function buildTransactionNotes(row: CashOperationRow): string;
10
+ export declare function buildInvestmentName(row: CashOperationRow): string;
11
+ export declare function formatQtyOutput(qty: string, instrument: string, qtyHadExplicitDecimal: boolean, product: string): string;
12
+ //# sourceMappingURL=operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAanD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAUxF;AAED,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,EAAE,OAAO,CAAC;CAClC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CA0BnE;AAeD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAUnE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAWjE;AAED,wBAAgB,eAAe,CAC3B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,qBAAqB,EAAE,OAAO,EAC9B,OAAO,EAAE,MAAM,GAChB,MAAM,CAUR"}
@@ -1,5 +1,3 @@
1
- import type { CashOperationRow } from "./types.ts";
2
-
3
1
  const TRANSACTION_TYPES = new Set([
4
2
  "Deposit",
5
3
  "Free funds interest",
@@ -8,106 +6,73 @@ const TRANSACTION_TYPES = new Set([
8
6
  "IKZE deposit",
9
7
  "Withholding tax",
10
8
  ]);
11
-
12
9
  const INVESTMENT_TYPES = new Set(["Stock purchase", "Stock sell"]);
13
-
14
- export function classifyOperation(type: string): "transaction" | "investment" | "ignored" {
10
+ export function classifyOperation(type) {
15
11
  if (TRANSACTION_TYPES.has(type)) {
16
12
  return "transaction";
17
13
  }
18
-
19
14
  if (INVESTMENT_TYPES.has(type)) {
20
15
  return "investment";
21
16
  }
22
-
23
17
  return "ignored";
24
18
  }
25
-
26
- export interface ParsedTradeValues {
27
- price: string;
28
- qty: string;
29
- qtyHadExplicitDecimal: boolean;
30
- }
31
-
32
- export function parseTradeValues(comment: string): ParsedTradeValues {
19
+ export function parseTradeValues(comment) {
33
20
  const normalized = comment.replace(/,/g, ".");
34
- const buySellMatch = normalized.match(
35
- /(?:\bBUY\b|\bSELL\b)\s+([+-]?\d+(?:\.\d+)?)(?:\/[+-]?\d+(?:\.\d+)?)?\s*@\s*([+-]?\d+(?:\.\d+)?)/i,
36
- );
37
-
21
+ const buySellMatch = normalized.match(/(?:\bBUY\b|\bSELL\b)\s+([+-]?\d+(?:\.\d+)?)(?:\/[+-]?\d+(?:\.\d+)?)?\s*@\s*([+-]?\d+(?:\.\d+)?)/i);
38
22
  const qtyMatch = buySellMatch
39
23
  ? [buySellMatch[0], buySellMatch[1]]
40
24
  : normalized.match(/\b([+-]?\d+(?:\.\d+)?)\b\s*@/);
41
25
  const priceMatch = buySellMatch ? [buySellMatch[0], buySellMatch[2]] : normalized.match(/@\s*([+-]?\d+(?:\.\d+)?)/);
42
-
43
26
  if (!qtyMatch || !priceMatch) {
44
27
  throw new Error(`Could not parse qty/price from comment: ${comment}`);
45
28
  }
46
-
47
29
  const rawQty = qtyMatch[1];
48
30
  const rawPrice = priceMatch[1];
49
31
  if (!rawQty || !rawPrice) {
50
32
  throw new Error(`Could not parse qty/price from comment: ${comment}`);
51
33
  }
52
-
53
34
  return {
54
35
  price: normalizeNumberString(rawPrice),
55
36
  qty: normalizeNumberString(rawQty),
56
37
  qtyHadExplicitDecimal: rawQty.includes("."),
57
38
  };
58
39
  }
59
-
60
- function normalizeNumberString(value: string): string {
40
+ function normalizeNumberString(value) {
61
41
  const parsed = Number(value);
62
42
  if (!Number.isFinite(parsed)) {
63
43
  throw new Error(`Invalid numeric value: ${value}`);
64
44
  }
65
-
66
45
  if (value.includes(".") && Number.isInteger(parsed)) {
67
46
  return parsed.toFixed(1);
68
47
  }
69
-
70
48
  return String(parsed);
71
49
  }
72
-
73
- export function buildTransactionNotes(row: CashOperationRow): string {
50
+ export function buildTransactionNotes(row) {
74
51
  const comment = row.Comment?.trim();
75
52
  const base = comment ? `XTB ${row.ID}: ${comment}` : `XTB ${row.ID}`;
76
53
  const product = row.Product?.trim();
77
-
78
54
  if (!product || product === "IKE" || product === "IKZE") {
79
55
  return base;
80
56
  }
81
-
82
57
  return `${base} [subaccount: ${product}]`;
83
58
  }
84
-
85
- export function buildInvestmentName(row: CashOperationRow): string {
59
+ export function buildInvestmentName(row) {
86
60
  if (row.Product?.trim() === "IKE") {
87
61
  return `XTB ${row.ID}: ${row.Type}`;
88
62
  }
89
-
90
63
  const instrument = row.Instrument?.trim();
91
64
  if (instrument) {
92
65
  return `${instrument} - ${row.Type} (XTB ${row.ID})`;
93
66
  }
94
-
95
67
  return `XTB ${row.ID}: ${row.Type}`;
96
68
  }
97
-
98
- export function formatQtyOutput(
99
- qty: string,
100
- instrument: string,
101
- qtyHadExplicitDecimal: boolean,
102
- product: string,
103
- ): string {
69
+ export function formatQtyOutput(qty, instrument, qtyHadExplicitDecimal, product) {
104
70
  if (product.trim() === "IKE") {
105
71
  return qtyHadExplicitDecimal ? qty : String(Number(qty));
106
72
  }
107
-
108
73
  if (qtyHadExplicitDecimal) {
109
74
  return qty;
110
75
  }
111
-
112
76
  return instrument.trim() ? `${qty}.0` : qty;
113
77
  }
78
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","sourceRoot":"","sources":["../src/operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { InvestmentRecord, TransactionRecord } from "./types.ts";
2
+ export declare function transactionsToCsv(records: TransactionRecord[]): string;
3
+ export declare function investmentsToCsv(records: InvestmentRecord[]): string;
4
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEtE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAKtE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAapE"}
package/dist/output.js ADDED
@@ -0,0 +1,16 @@
1
+ import { toCsv } from "./csv.js";
2
+ export function transactionsToCsv(records) {
3
+ return toCsv(["date", "amount", "name", "account", "notes"], records.map((record) => [record.date, record.amount, record.name, record.account, record.notes]));
4
+ }
5
+ export function investmentsToCsv(records) {
6
+ return toCsv(["date", "ticker", "currency", "qty", "price", "account", "name"], records.map((record) => [
7
+ record.date,
8
+ record.ticker,
9
+ record.currency,
10
+ record.qty,
11
+ record.price,
12
+ record.account,
13
+ record.name,
14
+ ]));
15
+ }
16
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAGjC,MAAM,UAAU,iBAAiB,CAAC,OAA4B;IAC1D,OAAO,KAAK,CACR,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAC9C,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CACnG,CAAC;AACN,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAA2B;IACxD,OAAO,KAAK,CACR,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,EACjE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI;QACX,MAAM,CAAC,MAAM;QACb,MAAM,CAAC,QAAQ;QACf,MAAM,CAAC,GAAG;QACV,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,OAAO;QACd,MAAM,CAAC,IAAI;KACd,CAAC,CACL,CAAC;AACN,CAAC"}
@@ -9,7 +9,6 @@ export interface CashOperationRow {
9
9
  Product: string;
10
10
  "Position ID": string;
11
11
  }
12
-
13
12
  export interface TransactionRecord {
14
13
  date: string;
15
14
  amount: string;
@@ -17,7 +16,6 @@ export interface TransactionRecord {
17
16
  account: string;
18
17
  notes: string;
19
18
  }
20
-
21
19
  export interface InvestmentRecord {
22
20
  date: string;
23
21
  ticker: string;
@@ -27,16 +25,16 @@ export interface InvestmentRecord {
27
25
  account: string;
28
26
  name: string;
29
27
  }
30
-
31
28
  interface ConversionIssue {
32
29
  rowIndex: number;
33
30
  operationId?: string;
34
31
  message: string;
35
32
  }
36
-
37
33
  export interface ConversionResult {
38
34
  transactions: TransactionRecord[];
39
35
  investments: InvestmentRecord[];
40
36
  ignoredRows: number;
41
37
  issues: ConversionIssue[];
42
38
  }
39
+ export {};
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC7B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@jaskrowo/xtb2sure",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Convert XTB .xlsx exports into Sure-compatible CSV files",
5
- "main": "src/index.ts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "engines": {
7
8
  "node": ">=24"
8
9
  },
@@ -16,19 +17,21 @@
16
17
  "url": "git+https://github.com/domeq/xtb2sure.git"
17
18
  },
18
19
  "files": [
19
- "src",
20
+ "dist",
20
21
  "bin"
21
22
  ],
22
23
  "bin": {
23
24
  "xtb2sure": "./bin/xtb2sure.js"
24
25
  },
25
26
  "scripts": {
27
+ "build": "tsc -p tsconfig.build.json",
26
28
  "dev": "node --watch src/index.ts",
27
29
  "convert": "node ./src/cli.ts convert",
28
30
  "format": "biome format --write .",
29
31
  "knip": "knip",
30
32
  "lint-fix": "biome check --write .",
31
33
  "lint": "biome check .",
34
+ "prepack": "npm run build",
32
35
  "prepare": "husky",
33
36
  "release": "release-it",
34
37
  "start": "node src/index.ts",
@@ -41,7 +44,7 @@
41
44
  ]
42
45
  },
43
46
  "imports": {
44
- "#*": "./src/*"
47
+ "#*": "./dist/*"
45
48
  },
46
49
  "devDependencies": {
47
50
  "@biomejs/biome": "2.5.15",
package/src/cli.ts DELETED
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import process from "node:process";
4
- import { Command, CommanderError } from "commander";
5
- import { convertFile } from "./index.ts";
6
-
7
- const EXIT_SUCCESS = 0;
8
- const EXIT_CONVERSION_FAILURE = 1;
9
- const EXIT_USAGE_ERROR = 2;
10
-
11
- interface ConvertCommandOptions {
12
- account: string;
13
- currency: string;
14
- dryRun: boolean;
15
- investmentsOut?: string;
16
- outDir?: string;
17
- report?: string;
18
- strict: boolean;
19
- transactionsOut?: string;
20
- }
21
-
22
- async function main(argv: string[]): Promise<number> {
23
- let exitCode = EXIT_SUCCESS;
24
-
25
- const program = new Command();
26
- program
27
- .name("xtb2sure")
28
- .description("Convert XTB .xlsx exports into Sure-compatible CSV files")
29
- .showHelpAfterError()
30
- .exitOverride();
31
-
32
- program
33
- .command("convert")
34
- .argument("<input.xlsx>", "Path to XTB export workbook")
35
- .requiredOption("--currency <code>", "Account currency for all investment rows")
36
- .requiredOption("--account <name>", "Account label used in generated CSV files")
37
- .option("--out-dir <dir>", "Output directory for default file names")
38
- .option("--transactions-out <file>", "Transactions CSV path")
39
- .option("--investments-out <file>", "Investments CSV path")
40
- .option("--strict", "Fail on first conversion issue", false)
41
- .option("--dry-run", "Do not write output files", false)
42
- .option("--report <file>", "Write conversion report JSON to file")
43
- .action(async (inputPath: string, options: ConvertCommandOptions) => {
44
- try {
45
- const result = await convertFile({
46
- account: options.account,
47
- currency: options.currency,
48
- dryRun: options.dryRun,
49
- inputPath,
50
- investmentsOut: options.investmentsOut,
51
- outDir: options.outDir,
52
- report: options.report,
53
- strict: options.strict,
54
- transactionsOut: options.transactionsOut,
55
- });
56
-
57
- const totals = result.reportData.totals;
58
- console.error(
59
- `Converted rows=${totals.sourceRows}, transactions=${totals.transactionRows}, investments=${totals.investmentRows}, ignored=${totals.ignoredRows}, issues=${totals.issues}`,
60
- );
61
-
62
- if (options.dryRun) {
63
- console.error("Dry run enabled: files were not written.");
64
- } else {
65
- console.error(`Transactions CSV: ${result.transactionsOutPath}`);
66
- console.error(`Investments CSV: ${result.investmentsOutPath}`);
67
- }
68
-
69
- if (totals.issues > 0 && options.strict) {
70
- exitCode = EXIT_CONVERSION_FAILURE;
71
- }
72
- } catch (error) {
73
- console.error(error instanceof Error ? error.message : String(error));
74
- exitCode = EXIT_CONVERSION_FAILURE;
75
- }
76
- });
77
-
78
- try {
79
- await program.parseAsync(argv, { from: "user" });
80
-
81
- return exitCode;
82
- } catch (error) {
83
- if (error instanceof CommanderError) {
84
- if (error.code === "commander.helpDisplayed") {
85
- return EXIT_SUCCESS;
86
- }
87
-
88
- return EXIT_USAGE_ERROR;
89
- }
90
-
91
- console.error(error instanceof Error ? error.message : String(error));
92
- return EXIT_CONVERSION_FAILURE;
93
- }
94
- }
95
-
96
- main(process.argv.slice(2))
97
- .then((code) => {
98
- process.exitCode = code;
99
- })
100
- .catch((error) => {
101
- console.error(error instanceof Error ? error.message : String(error));
102
- process.exitCode = EXIT_CONVERSION_FAILURE;
103
- });
package/src/output.ts DELETED
@@ -1,24 +0,0 @@
1
- import { toCsv } from "./csv.ts";
2
- import type { InvestmentRecord, TransactionRecord } from "./types.ts";
3
-
4
- export function transactionsToCsv(records: TransactionRecord[]): string {
5
- return toCsv(
6
- ["date", "amount", "name", "account", "notes"],
7
- records.map((record) => [record.date, record.amount, record.name, record.account, record.notes]),
8
- );
9
- }
10
-
11
- export function investmentsToCsv(records: InvestmentRecord[]): string {
12
- return toCsv(
13
- ["date", "ticker", "currency", "qty", "price", "account", "name"],
14
- records.map((record) => [
15
- record.date,
16
- record.ticker,
17
- record.currency,
18
- record.qty,
19
- record.price,
20
- record.account,
21
- record.name,
22
- ]),
23
- );
24
- }