Working cli and browser versions

This commit is contained in:
Kuba Orlik 2023-12-10 15:15:13 +01:00
parent 84a9b530d4
commit 31b41a3857
13 changed files with 907 additions and 55 deletions

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# Mbank mt940 converter
## Testowanie
Wygeneruj raz plik mt940 oraz csv za ten sam okres, i wykonaj:
```
node . convert < tests/real_csv.csv > tests/new.txt && diff --side-by-side --color tests/real_mt940.txt tests/new.txt
```

4
captain-definition Normal file
View File

@ -0,0 +1,4 @@
{
"schemaVersion": 2,
"templateId": "node/20"
}

525
package-lock.json generated
View File

@ -1,18 +1,25 @@
{
"name": "module-starter",
"version": "0.0.1",
"name": "mbank-mt940",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "module-starter",
"version": "0.0.1",
"name": "mbank-mt940",
"version": "0.1.0",
"license": "ISC",
"dependencies": {
"iconv": "^3.0.1",
"iconv-lite": "^0.6.3",
"yargs": "^17.7.2"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/mocha": "^10.0.1",
"@types/yargs": "^17.0.32",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"esbuild": "^0.19.9",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
@ -473,6 +480,358 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz",
"integrity": "sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz",
"integrity": "sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.9.tgz",
"integrity": "sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz",
"integrity": "sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz",
"integrity": "sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz",
"integrity": "sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz",
"integrity": "sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz",
"integrity": "sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz",
"integrity": "sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz",
"integrity": "sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz",
"integrity": "sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz",
"integrity": "sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz",
"integrity": "sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz",
"integrity": "sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz",
"integrity": "sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz",
"integrity": "sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz",
"integrity": "sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz",
"integrity": "sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz",
"integrity": "sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz",
"integrity": "sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz",
"integrity": "sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz",
"integrity": "sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@ -831,6 +1190,21 @@
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"dev": true
},
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
"dev": true,
"dependencies": {
"@types/yargs-parser": "*"
}
},
"node_modules/@types/yargs-parser": {
"version": "21.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz",
@ -1406,7 +1780,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -1415,7 +1788,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -1664,21 +2036,22 @@
}
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@ -1689,8 +2062,7 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/commondir": {
"version": "1.0.1",
@ -1825,8 +2197,7 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/es6-error": {
"version": "4.1.1",
@ -1834,11 +2205,47 @@
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true
},
"node_modules/esbuild": {
"version": "0.19.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.9.tgz",
"integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.19.9",
"@esbuild/android-arm64": "0.19.9",
"@esbuild/android-x64": "0.19.9",
"@esbuild/darwin-arm64": "0.19.9",
"@esbuild/darwin-x64": "0.19.9",
"@esbuild/freebsd-arm64": "0.19.9",
"@esbuild/freebsd-x64": "0.19.9",
"@esbuild/linux-arm": "0.19.9",
"@esbuild/linux-arm64": "0.19.9",
"@esbuild/linux-ia32": "0.19.9",
"@esbuild/linux-loong64": "0.19.9",
"@esbuild/linux-mips64el": "0.19.9",
"@esbuild/linux-ppc64": "0.19.9",
"@esbuild/linux-riscv64": "0.19.9",
"@esbuild/linux-s390x": "0.19.9",
"@esbuild/linux-x64": "0.19.9",
"@esbuild/netbsd-x64": "0.19.9",
"@esbuild/openbsd-x64": "0.19.9",
"@esbuild/sunos-x64": "0.19.9",
"@esbuild/win32-arm64": "0.19.9",
"@esbuild/win32-ia32": "0.19.9",
"@esbuild/win32-x64": "0.19.9"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -2376,7 +2783,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@ -2518,6 +2924,26 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/iconv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/iconv/-/iconv-3.0.1.tgz",
"integrity": "sha512-lJnFLxVc0d82R7GfU7a9RujKVUQ3Eee19tPKWZWBJtAEGRHVEyFzCtbNl3GPKuDnHBBRT4/nDS4Ru9AIDT72qA==",
"hasInstallScript": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2602,7 +3028,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -3052,6 +3477,17 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/mocha/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@ -3097,6 +3533,24 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/mocha/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@ -3667,7 +4121,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -3755,6 +4208,11 @@
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz",
@ -3885,7 +4343,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@ -3899,7 +4356,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -4190,7 +4646,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@ -4225,7 +4680,6 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"engines": {
"node": ">=10"
}
@ -4237,21 +4691,20 @@
"dev": true
},
"node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dependencies": {
"cliui": "^7.0.2",
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=10"
"node": ">=12"
}
},
"node_modules/yargs-parser": {
@ -4302,6 +4755,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yargs/node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View File

@ -1,24 +1,29 @@
{
"name": "module-starter",
"version": "0.0.1",
"name": "mbank-mt940",
"version": "0.1.0",
"description": "module template",
"main": "lib/index.js",
"main": "lib/cli.js",
"scripts": {
"test": "mocha",
"build": "tsc",
"build": "npm run build-node && npm run build-browser",
"build-node": "tsc",
"build-browser": "esbuild src/browser.ts --bundle --minify --sourcemap --target=firefox120 --outfile=public/bundle.js",
"prepare": "npm run build",
"clean-coverage": "rm -rf coverage .nyc_output .xunit",
"coverage": "npm run clean-coverage && nyc mocha",
"test-reports": "npm run clean-coverage && nyc --reporter clover mocha --reporter xunit --reporter-option output=.xunit",
"coverage-html": "npm run test-reports && nyc report --reporter lcov && xdg-open coverage/lcov-report/index.html"
"coverage-html": "npm run test-reports && nyc report --reporter lcov && xdg-open coverage/lcov-report/index.html",
"start": "http-server public -p 8080"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/mocha": "^10.0.1",
"@types/yargs": "^17.0.32",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"esbuild": "^0.19.9",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
@ -31,5 +36,10 @@
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"types": "./@types/index.d.ts"
"types": "./@types/index.d.ts",
"dependencies": {
"iconv": "^3.0.1",
"iconv-lite": "^0.6.3",
"yargs": "^17.7.2"
}
}

22
public/bundle.js Normal file
View File

@ -0,0 +1,22 @@
(()=>{function l(e,n){let t=[];for(let o=0;o<e.length;o+=n){let r=e.slice(o,o+n);t.push(r)}return t}function p(e,n){let t=[...e];for(;t.length<n;)t.push("");return t}function m(e,n,t="~"){return e.map((o,r)=>`${t}${n+r}${o}`)}function _(e){return e.replace(/ +/g," ")}var g=class{constructor(n,t,o,r){this.number=n;this.initial_balance=t;this.closing_balance=o;this.currency=r}},d=class e{constructor(n,t,o,r,s,i,c,a){this.acc_date=n;this.op_date=t;this.description=o;this.title=r;this.person=s;this.account_number=i;this.amount=c;this.balance_after=a}static{this.counter=function*(){let n=1;for(;;)yield n.toString().padStart(11,"0"),n++}()}formatTitle(){return m(p(l(this.title.trim(),27),9),20).join(`
`)}formatPerson(){return m(p(l(_(this.person).trim(),27),2).slice(0,2),32).join(`
`)}toMT940(){let n={incoming:"150",outcoming:"169"},t=this.amount>0?n.incoming:n.outcoming;return`:61:${u(this.acc_date)}${u(this.op_date).slice(2)}C${$(this.amount)}S${t}${e.counter.next().value}
:86:${t}
:86:${t}~00B${t}${this.description.slice(0,23)}
${this.formatTitle()}
~29${this.account_number}
~30${this.account_number.slice(2,10)}
~31${this.account_number.slice(10)}
${this.formatPerson()}
~34${t}
~38PL${this.account_number}
~62
~63`}},b=class{constructor(n,t){this.date_start=n;this.date_end=t}},h=class{},f=class extends h{read_csv(n){let t=n.split(`\r
`).map(c=>c.split(";")),o=0;for(;!t[o][0].startsWith("Niniejszy dokument sporz\u0105dzono");)o++;let r=new g(t[20][0],this.parseAmount(t[35][1]),this.parseAmount(t[o-2][7]),t[18][0]),s=new b(this.parseDate(t[14][0]),this.parseDate(t[14][1])),i=[];for(let c=38;c<=o-5;c++){let a=t[c];if(a.length!=9)throw new Error("Wrong amount of columns! maybe a semicolon got stuck in a transaction description?");let v=new Date(a[0]),y=new Date(a[1]),[D,S,x,L]=a.slice(2).map(this.trimString),[M,E]=a.slice(6).map(T=>this.parseAmount(T));i.push(new d(v,y,D,S,x,L,M,E))}return{account:r,range:s,transactions:i}}parseAmount(n){return parseFloat(n.replace(/[^0-9,-]/g,"").replace(",","."))}parseDate(n){let[t,o,r]=n.split(".").map(i=>parseInt(i)),s=new Date;return s.setHours(0),s.setMinutes(0),s.setSeconds(0),s.setMilliseconds(0),s.setDate(t),s.setMonth(o-1),s.setFullYear(r),s}trimString(n){return n.replaceAll(/(^["']|["']$)/g,"")}};function u(e){return`${e.getFullYear()-2e3}${(e.getMonth()+1).toString().padStart(2,"0")}${e.getDate().toString().padStart(2,"0")}`}function $(e){return Math.abs(e).toString().replace(".",",")}function w(e){let n=new f().read_csv(e),{account:t,transactions:o,range:r}=n;return{output:`:20:MT940
:25:/PL${t.number.replaceAll(/[^0-9]/g,"")}
:28C:${u(r.date_start)}
:60F:D${u(r.date_start)}${t.currency}${$(t.initial_balance)}
${o.map(i=>i.toMT940()).join(`
`)}
:62F:D${u(r.date_end)}${t.currency}${$(t.closing_balance)}`,range:r}}function A(e,n,t){var o=new Blob([e],{type:t}),r=document.createElement("a"),s=URL.createObjectURL(o);r.href=s,r.download=n,document.body.appendChild(r),r.click(),setTimeout(function(){document.body.removeChild(r),window.URL.revokeObjectURL(s)},0)}async function R(){console.log("browser.ts:2");let e=document.getElementById("csv");if(!e||!(e instanceof HTMLInputElement))throw console.log("browser.ts:5"),new Error("coudl not find the csv input");var n=e.value.replace("C:\\fakepath\\","");let t=e.files;if(!t)throw new Error("no files in the input");let o=await t[0].arrayBuffer(),r=new TextDecoder("windows-1250").decode(o),s=w(r);A(s.output,`raport-${s.range.date_start.getFullYear()}-${(s.range.date_start.getMonth()+1).toString().padStart(2,"0")}.mt940`,"text")}document.querySelector("#submit")?.addEventListener("click",R);console.log("added handler to",document.querySelector("#submit"));})();
//# sourceMappingURL=bundle.js.map

7
public/bundle.js.map Normal file

File diff suppressed because one or more lines are too long

40
public/index.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>mBank mt940 konwerter</title>
</head>
<style>
p {
max-width: 600px;
}
input,
button {
height: 40px;
font-size: 20px;
}
</style>
<body>
<h1>mBank mt940 konwerter</h1>
<p>
Za pomocą tego narzędzia za darmo przekonwertujesz plik CSV
wygenerowany przez mbank do formatu mt940. Konwersja odbywa sie w
przeglądarce, Twoje dane nie są nigdzie wysyłane. Dla pewności
możesz na czas konwersji wyłączyć dostęp do Internetu na Twoim
komputerze, zamknąć tę stronę, i włączyć dostęp do Internetu
ponownie.
</p>
<div><input type="file" name="csv" id="csv" /></div>
<button id="submit">Konwertuj</button>
<p>
Aplikacja została wykonana przez
<a href="https://www.internet-czas-dzialac.pl/">
Fundację „Internet. Czas działać!”
</a>
i jest utrzymywana przez
<a href="https://www.sealcode.it/">Sealcode</a>
</p>
<script src="bundle.js"></script>
</body>
</html>

44
src/browser.ts Normal file
View File

@ -0,0 +1,44 @@
import { convert } from ".";
function download(data: string, filename: string, type: string) {
var file = new Blob([data], { type: type });
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
async function handle() {
console.log("browser.ts:2");
const csv_input = document.getElementById("csv");
if (!csv_input || !(csv_input instanceof HTMLInputElement)) {
console.log("browser.ts:5");
throw new Error("coudl not find the csv input");
}
var fileName = csv_input.value.replace("C:\\fakepath\\", "");
const files = csv_input.files;
if (!files) {
throw new Error("no files in the input");
}
const buffer = await files[0].arrayBuffer();
const string = new TextDecoder("windows-1250").decode(buffer);
const result = convert(string);
download(
result.output,
`raport-${result.range.date_start.getFullYear()}-${(
result.range.date_start.getMonth() + 1
)
.toString()
.padStart(2, "0")}.mt940`,
"text"
);
}
document.querySelector("#submit")?.addEventListener("click", handle);
console.log("added handler to", document.querySelector("#submit"));

57
src/cli.ts Normal file
View File

@ -0,0 +1,57 @@
import { promises as fs } from "fs";
import iconv from "iconv-lite";
import yargs from "yargs";
import { convert } from ".";
const path = process.argv.at(-1);
if (!path) {
console.error("Podaj ścieżkę do pliku jako argument w CLI");
process.exit();
}
yargs
.scriptName("mbank-mt940")
.usage("$0 <cmd> [args]")
.command(
"convert",
"onvert from csv to mt940",
(yargs) => {
yargs.option("i", {
alias: "input",
type: "string",
describe: "path to the mbank csv file",
demandOption: false,
});
yargs.option("o", {
alias: "output",
type: "string",
describe: "where to save the mt940 file",
demandOption: false,
});
},
async function (argv) {
let content: Buffer;
const has_path = argv.input && argv.input !== "--";
if (has_path) {
console.error("Reading from ", argv.input);
content = await fs.readFile(argv.input as string, {
encoding: null,
});
} else {
console.error("reading CSV from stdin...");
content = await new Promise((resolve, reject) => {
let chunks: Buffer[] = [];
process.stdin.on("data", (chunk) => {
chunks.push(chunk);
});
process.stdin.on("end", () => {
const buffer = Buffer.concat(chunks);
resolve(buffer);
});
});
}
const result = convert(iconv.decode(content, "cp1250"));
console.log(result);
}
)
.help().argv;

View File

@ -1,11 +0,0 @@
import { Example } from "./index";
import * as assert from "assert";
describe("Example", () => {
describe("example", () => {
it("should equal 'example'", () => {
const example = new Example();
assert.equal(example.example(), "example");
});
});
});

View File

@ -1,5 +1,186 @@
export class Example {
example(): string {
return "example";
import {
addLineNumbers,
chunks,
fillWithEmpty,
removeRepeatingSpace,
} from "./utils";
class Account {
constructor(
public number: string,
public initial_balance: number,
public closing_balance: number,
public currency: string
) {}
}
class Transaction {
constructor(
public acc_date: Date,
public op_date: Date,
public description: string,
public title: string,
public person: string,
public account_number: string,
public amount: number,
public balance_after: number
) {}
public static counter = (function* () {
let c = 1;
while (true) {
yield c.toString().padStart(11, "0");
c++;
}
})();
formatTitle() {
return addLineNumbers(
fillWithEmpty(chunks(this.title.trim(), 27), 9),
20
).join("\n");
}
formatPerson() {
return addLineNumbers(
fillWithEmpty(
chunks(removeRepeatingSpace(this.person).trim(), 27),
2
).slice(0, 2),
32
).join("\n");
}
toMT940() {
const prefixes = { incoming: "150", outcoming: "169" };
const prefix = this.amount > 0 ? prefixes.incoming : prefixes.outcoming;
return `:61:${mtDate(this.acc_date)}${mtDate(this.op_date).slice(
2
)}C${mtAmount(this.amount)}S${prefix}${Transaction.counter.next().value}
:86:${prefix}
:86:${prefix}~00B${prefix}${this.description.slice(0, 23)}
${this.formatTitle()}
~29${this.account_number}
~30${this.account_number.slice(2, 10)}
~31${this.account_number.slice(10)}
${this.formatPerson()}
~34${prefix}
~38PL${this.account_number}
~62
~63`;
}
}
class Range {
constructor(public date_start: Date, public date_end: Date) {}
}
abstract class CSVParser {
abstract read_csv(content_utf8: string): {
account: Account;
range: Range;
transactions: Transaction[];
};
}
class mBankParser extends CSVParser {
read_csv(content_utf8: string): {
account: Account;
range: Range;
transactions: Transaction[];
} {
const lines = content_utf8.split("\r\n").map((line) => line.split(";"));
let last = 0;
while (!lines[last][0].startsWith("Niniejszy dokument sporządzono")) {
last++;
}
const account = new Account(
lines[20][0],
this.parseAmount(lines[35][1]),
this.parseAmount(lines[last - 2][7]),
lines[18][0]
);
const range = new Range(
this.parseDate(lines[14][0]),
this.parseDate(lines[14][1])
);
const transactions = [];
for (let i = 38; i <= last - 5; i++) {
const line = lines[i];
if (line.length != 9) {
throw new Error(
"Wrong amount of columns! maybe a semicolon got stuck in a transaction description?"
);
}
const date_acc = new Date(line[0]);
const date_op = new Date(line[1]);
const [description, title, person, account_number] = line
.slice(2)
.map(this.trimString);
const [amount, balance_after] = line
.slice(6)
.map((s) => this.parseAmount(s));
transactions.push(
new Transaction(
date_acc,
date_op,
description,
title,
person,
account_number,
amount,
balance_after
)
);
}
return { account, range, transactions };
}
parseAmount(s: string): number {
return parseFloat(s.replace(/[^0-9,-]/g, "").replace(",", "."));
}
parseDate(s: string): Date {
const [day, month, year] = s.split(".").map((s) => parseInt(s));
const d = new Date();
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
d.setDate(day);
d.setMonth(month - 1);
d.setFullYear(year);
return d;
}
trimString(s: string): string {
//removes redundant quotes at the beginning or end of the transaction
return s.replaceAll(/(^["']|["']$)/g, "");
}
}
function mtDate(d: Date) {
return `${d.getFullYear() - 2000}${(d.getMonth() + 1)
.toString()
.padStart(2, "0")}${d.getDate().toString().padStart(2, "0")}`;
}
function mtAmount(n: number) {
return Math.abs(n).toString().replace(".", ",");
}
export function convert(csv_utf8: string): { output: string; range: Range } {
const result = new mBankParser().read_csv(csv_utf8);
const { account, transactions, range } = result;
const string = `:20:MT940
:25:/PL${account.number.replaceAll(/[^0-9]/g, "")}
:28C:${mtDate(range.date_start)}
:60F:D${mtDate(range.date_start)}${account.currency}${mtAmount(
account.initial_balance
)}
${transactions.map((t) => t.toMT940()).join("\n")}
:62F:D${mtDate(range.date_end)}${account.currency}${mtAmount(
account.closing_balance
)}`;
return { output: string, range };
}

28
src/utils.ts Normal file
View File

@ -0,0 +1,28 @@
export function chunks(s: string, chunkSize: number): string[] {
const result = [];
for (let i = 0; i < s.length; i += chunkSize) {
const chunk = s.slice(i, i + chunkSize);
result.push(chunk);
}
return result;
}
export function fillWithEmpty(array: string[], target_length: number) {
const a = [...array];
while (a.length < target_length) {
a.push("");
}
return a;
}
export function addLineNumbers(
s: string[],
start_num: number,
prefix = "~"
): string[] {
return s.map((l, i) => `${prefix}${start_num + i}${l}`);
}
export function removeRepeatingSpace(s: string) {
return s.replace(/ +/g, " ");
}

View File

@ -5,10 +5,10 @@
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"target": "ES6",
"target": "ES2020",
"declaration": true,
"esModuleInterop": true,
"lib": ["ES6", "ESNext"],
"lib": ["ES6", "ESNext", "DOM"],
"outDir": "lib",
"checkJs": true,
"allowJs": true,
@ -17,6 +17,6 @@
"sourceMap": true,
"skipLibCheck": true
},
"include": ["src/**/*", ],
"include": ["src/**/*"],
"exclude": ["node_modules/**"]
}