@jaskrowo/xtb2sure 0.2.2 → 0.2.3
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 +20 -16
- package/bin/xtb2sure.js +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +81 -0
- package/dist/cli.js.map +1 -0
- package/dist/convert.d.ts +8 -0
- package/dist/convert.d.ts.map +1 -0
- package/{src/convert.ts → dist/convert.js} +15 -40
- package/dist/convert.js.map +1 -0
- package/dist/csv.d.ts +2 -0
- package/dist/csv.d.ts.map +1 -0
- package/{src/csv.ts → dist/csv.js} +2 -2
- package/dist/csv.js.map +1 -0
- package/dist/date.d.ts +2 -0
- package/dist/date.d.ts.map +1 -0
- package/{src/date.ts → dist/date.js} +4 -7
- package/dist/date.js.map +1 -0
- package/dist/excel.d.ts +3 -0
- package/dist/excel.d.ts.map +1 -0
- package/{src/excel.ts → dist/excel.js} +15 -35
- package/dist/excel.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +7 -47
- package/dist/index.js.map +1 -0
- package/dist/operations.d.ts +12 -0
- package/dist/operations.d.ts.map +1 -0
- package/{src/operations.ts → dist/operations.js} +8 -43
- package/dist/operations.js.map +1 -0
- package/dist/output.d.ts +4 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +16 -0
- package/dist/output.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +2 -4
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -4
- package/src/cli.ts +0 -103
- package/src/output.ts +0 -24
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
|
-
|
|
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
|
-
|
|
44
|
+
xtb2sure convert report.xlsx --currency USD --account "XTB IKE"
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
```bash
|
|
44
|
-
|
|
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
package/dist/cli.d.ts
ADDED
|
@@ -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
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
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 @@
|
|
|
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"}
|
package/dist/csv.js.map
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/date.js.map
ADDED
|
@@ -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"}
|
package/dist/excel.d.ts
ADDED
|
@@ -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
|
-
]
|
|
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
|
|
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
|
|
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
|
-
}
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
70
55
|
console.error = originalConsoleError;
|
|
71
56
|
}
|
|
72
57
|
}
|
|
73
|
-
|
|
74
|
-
|
|
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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.
|
|
4
|
-
import { readCashOperationsSheet } from "./excel.
|
|
5
|
-
import { investmentsToCsv, transactionsToCsv } from "./output.
|
|
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
|
-
|
|
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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"}
|
package/dist/output.d.ts
ADDED
|
@@ -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 @@
|
|
|
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.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Convert XTB .xlsx exports into Sure-compatible CSV files",
|
|
5
|
-
"main": "
|
|
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
|
-
"
|
|
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
|
-
"#*": "./
|
|
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
|
-
}
|