Merge pull request 'Make UI improvements' (#1) from mtsz/mt940-mbank-ts:ui-improvements into master
Reviewed-on: #1
This commit is contained in:
		
						commit
						5f86deeb0a
					
				
							
								
								
									
										17
									
								
								.arcconfig
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								.arcconfig
									
									
									
									
									
								
							| @ -1,12 +1,7 @@ | |||||||
| { | { | ||||||
|   "phabricator.uri": "https://hub.sealcode.org/", | 	"phabricator.uri": "https://hub.sealcode.org/", | ||||||
|   "arc.land.onto.default": "hotwire", | 	"arc.land.onto.default": "hotwire", | ||||||
|   "load": [ | 	"load": ["arcanist-linters", "arc-unit-mocha/src"], | ||||||
|     "arcanist-linters", | 	"unit.engine": "MochaEngine", | ||||||
|     "arc-unit-mocha/src" | 	"unit.mocha.include": [".test/**/*.ts"] | ||||||
|   ], | } | ||||||
|   "unit.engine": "MochaEngine", |  | ||||||
|   "unit.mocha.include": [ |  | ||||||
|     ".test/**/*.ts" |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -6,5 +6,8 @@ lib | |||||||
| coverage | coverage | ||||||
| @types | @types | ||||||
| 
 | 
 | ||||||
| public/*.js | public/bundle.* | ||||||
| /public/bundle.js.map | public/*.woff | ||||||
|  | public/*.woff2 | ||||||
|  | 
 | ||||||
|  | .DS_Store | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								.prettierrc
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								.prettierrc
									
									
									
									
									
								
							| @ -1,14 +1,7 @@ | |||||||
| { | { | ||||||
|   useTabs: true, | 	"useTabs": true, | ||||||
|   tabWidth: 4, | 	"tabWidth": 4, | ||||||
|   trailingComma: "es5", | 	"trailingComma": "es5", | ||||||
|   "overrides": [ | 	"printWidth": 120, | ||||||
|     { | 	"htmlWhitespaceSensitivity": "ignore" | ||||||
|       "files": "*.yml", | } | ||||||
|       "options": { |  | ||||||
|         "tabWidth": 2, |  | ||||||
|         "useTabs": false |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| # Mbank mt940 converter | # mBank MT940 converter | ||||||
| 
 | 
 | ||||||
| ## Uruchamianie | ## Uruchamianie | ||||||
| 
 | 
 | ||||||
| @ -9,7 +9,7 @@ npm start | |||||||
| 
 | 
 | ||||||
| ## Testowanie | ## Testowanie | ||||||
| 
 | 
 | ||||||
| Wygeneruj raz plik mt940 oraz csv za ten sam okres, i wykonaj: | 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 | node . convert < tests/real_csv.csv > tests/new.txt && diff --side-by-side --color tests/real_mt940.txt tests/new.txt | ||||||
|  | |||||||
							
								
								
									
										943
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										943
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							| @ -7,13 +7,18 @@ | |||||||
| 		"test": "mocha", | 		"test": "mocha", | ||||||
| 		"build": "npm run build-node && npm run build-browser", | 		"build": "npm run build-node && npm run build-browser", | ||||||
| 		"build-node": "tsc", | 		"build-node": "tsc", | ||||||
| 		"build-browser": "esbuild src/browser.ts --bundle --minify --sourcemap --target=firefox120 --outfile=public/bundle.js", | 		"build-browser": "esbuild src/browser.ts --bundle --minify --sourcemap --target=firefox120 --outfile=public/bundle.js --loader:.woff=file --loader:.woff2=file", | ||||||
|  | 		"watch-node": "tsc --watch", | ||||||
|  | 		"watch-browser": "esbuild src/browser.ts --bundle --outfile=public/bundle.js --watch --loader:.woff=file --loader:.woff2=file", | ||||||
| 		"postinstall": "npm run build", | 		"postinstall": "npm run build", | ||||||
| 		"clean-coverage": "rm -rf coverage .nyc_output .xunit", | 		"clean-coverage": "rm -rf coverage .nyc_output .xunit", | ||||||
| 		"coverage": "npm run clean-coverage && nyc mocha", | 		"coverage": "npm run clean-coverage && nyc mocha", | ||||||
| 		"test-reports": "npm run clean-coverage && nyc --reporter clover mocha --reporter xunit --reporter-option output=.xunit", | 		"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" | 		"start": "http-server public -p 8080", | ||||||
|  | 		"dev": "concurrently --names \"NODE,BROWSER,SERVER\" --prefix-colors \"blue,magenta,green\" \"npm run watch-node\" \"npm run watch-browser\" \"npm run start\"", | ||||||
|  | 		"format": "prettier --write .", | ||||||
|  | 		"format:check": "prettier --check ." | ||||||
| 	}, | 	}, | ||||||
| 	"author": "", | 	"author": "", | ||||||
| 	"license": "ISC", | 	"license": "ISC", | ||||||
| @ -22,6 +27,7 @@ | |||||||
| 		"@types/diff": "^5.2.1", | 		"@types/diff": "^5.2.1", | ||||||
| 		"@typescript-eslint/eslint-plugin": "^5.58.0", | 		"@typescript-eslint/eslint-plugin": "^5.58.0", | ||||||
| 		"@typescript-eslint/parser": "^5.58.0", | 		"@typescript-eslint/parser": "^5.58.0", | ||||||
|  | 		"concurrently": "^9.2.0", | ||||||
| 		"diff": "^5.2.0", | 		"diff": "^5.2.0", | ||||||
| 		"eslint": "^8.38.0", | 		"eslint": "^8.38.0", | ||||||
| 		"eslint-config-prettier": "^8.8.0", | 		"eslint-config-prettier": "^8.8.0", | ||||||
| @ -36,6 +42,7 @@ | |||||||
| 	}, | 	}, | ||||||
| 	"types": "./@types/index.d.ts", | 	"types": "./@types/index.d.ts", | ||||||
| 	"dependencies": { | 	"dependencies": { | ||||||
|  | 		"@fontsource/atkinson-hyperlegible": "^5.2.6", | ||||||
| 		"@types/mocha": "^10.0.1", | 		"@types/mocha": "^10.0.1", | ||||||
| 		"@types/node": "^20.10.4", | 		"@types/node": "^20.10.4", | ||||||
| 		"@types/yargs": "^17.0.32", | 		"@types/yargs": "^17.0.32", | ||||||
|  | |||||||
| @ -1,68 +1,77 @@ | |||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html> | <html lang="pl"> | ||||||
| 	<head> | 	<head> | ||||||
| 		<title>mBank mt940 konwerter</title> | 		<title>mBank MT940 konwerter</title> | ||||||
|  | 		<meta charset="utf-8" /> | ||||||
|  | 		<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||||
|  | 		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css" /> | ||||||
|  | 		<link rel="stylesheet" href="styles.css" /> | ||||||
| 	</head> | 	</head> | ||||||
| 	<style> |  | ||||||
| 		p { |  | ||||||
| 			max-width: 600px; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		input, |  | ||||||
| 		button { |  | ||||||
| 			height: 40px; |  | ||||||
| 			font-size: 20px; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		.hidden { |  | ||||||
| 			display: none; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		.success { |  | ||||||
| 			background-color: hsl(120, 100%, 85.1%); |  | ||||||
| 			padding: 2rem; |  | ||||||
| 			margin: 1rem; |  | ||||||
| 			font-size: 1.2rem; |  | ||||||
| 			color: hsl(120, 100%, 7.1%); |  | ||||||
| 		} |  | ||||||
| 	</style> |  | ||||||
| 	<body> | 	<body> | ||||||
| 		<h1>mBank mt940 konwerter</h1> | 		<header> | ||||||
|  | 			<section> | ||||||
|  | 				<h1>mBank MT940 konwerter</h1> | ||||||
|  | 			</section> | ||||||
|  | 		</header> | ||||||
|  | 		<main> | ||||||
|  | 			<section> | ||||||
|  | 				<p> | ||||||
|  | 					Za pomocą tego narzędzia za darmo przekonwertujesz plik CSV wygenerowany przez mBank do formatu | ||||||
|  | 					MT940. Konwersja odbywa się 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> | ||||||
| 
 | 
 | ||||||
| 		<p> | 				<form> | ||||||
| 			Za pomocą tego narzędzia za darmo przekonwertujesz plik CSV | 					<fieldset> | ||||||
| 			wygenerowany przez mbank do formatu mt940. Konwersja odbywa sie w | 						<label for="csv">Wybierz plik CSV z mBanku:</label> | ||||||
| 			przeglądarce, Twoje dane nie są nigdzie wysyłane. Dla pewności | 						<input type="file" name="csv" id="csv" accept=".csv" /> | ||||||
| 			możesz na czas konwersji wyłączyć dostęp do Internetu na Twoim | 					</fieldset> | ||||||
| 			komputerze, zamknąć tę stronę, i włączyć dostęp do Internetu | 					<button type="button" id="submit">Konwertuj</button> | ||||||
| 			ponownie. | 				</form> | ||||||
| 		</p> | 
 | ||||||
| 		<div><input type="file" name="csv" id="csv" /></div> | 				<div class="success hidden" role="alert"> | ||||||
| 		<button id="submit">Konwertuj</button> | 					<p> | ||||||
| 		<div class="success hidden"> | 						<strong>Sukces!</strong> | ||||||
| 			<p> | 						Rozpoczęto pobieranie wygenerowanego raportu MT940. Cieszymy się, że mogliśmy Ci oszczędzić | ||||||
| 				Rozpoczęto pobieranie wygenerowanego raportu mt940. Cieszymy | 						trochę wydatków. Jeżeli chcesz się nam odwdzięczyć, zachęcamy do | ||||||
| 				się, że mogliśmy Ci oszczędzić trochę wydatków. Jeżeli chcesz | 						<a target="_blank" href="https://www.internet-czas-dzialac.pl/contact/#wesprzyj-nas"> | ||||||
| 				się nam odwdzięczyć, zachęcamy do | 							wspierania naszej fundacji | ||||||
| 				<a | 						</a> | ||||||
| 					href="https://www.internet-czas-dzialac.pl/contact/#wesprzyj-nas" | 					</p> | ||||||
| 					>wspierania naszej fundacji</a | 				</div> | ||||||
| 				> | 			</section> | ||||||
| 			</p> | 		</main> | ||||||
| 		</div> | 
 | ||||||
| 		<p> | 		<footer> | ||||||
| 			Aplikacja została wykonana przez | 			<section> | ||||||
| 			<a href="https://www.internet-czas-dzialac.pl/"> | 				<a target="_blank" href="https://www.internet-czas-dzialac.pl/"> | ||||||
| 				Fundację „Internet. Czas działać!” | 					<figure id="icd-logo-container"> | ||||||
| 			</a> | 						<div id="icd-logo"></div> | ||||||
| 			i jest utrzymywana przez | 					</figure> | ||||||
| 			<a href="https://www.sealcode.it/">Sealcode</a> | 				</a> | ||||||
| 		</p> | 
 | ||||||
| 		<p> | 				<p> | ||||||
| 			<a href="https://git.internet-czas-dzialac.pl/icd/mt940-mbank-ts"> | 					Aplikacja została wykonana przez | ||||||
| 				Kod źródłowy | 					<a target="_blank" href="https://www.internet-czas-dzialac.pl/"> | ||||||
| 			</a> | 						Fundację „Internet. Czas działać!” | ||||||
| 			<script src="bundle.js"></script> | 					</a> | ||||||
| 		</p> | 					<br /> | ||||||
|  | 					i jest utrzymywana przez | ||||||
|  | 					<a target="_blank" href="https://www.sealcode.it/">Sealcode</a> | ||||||
|  | 					• | ||||||
|  | 					<a target="_blank" href="https://git.internet-czas-dzialac.pl/icd/mt940-mbank-ts">Kod źródłowy</a> | ||||||
|  | 				</p> | ||||||
|  | 
 | ||||||
|  | 				<p> | ||||||
|  | 					<small> | ||||||
|  | 						Wykorzystuje czcionkę | ||||||
|  | 						<a target="_blank" href="https://brailleinstitute.org/freefont">Atkinson Hyperlegible</a> | ||||||
|  | 						na licencji SIL Open Font License | ||||||
|  | 					</small> | ||||||
|  | 				</p> | ||||||
|  | 			</section> | ||||||
|  | 		</footer> | ||||||
|  | 		<script src="bundle.js"></script> | ||||||
| 	</body> | 	</body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 30 KiB | 
							
								
								
									
										96
									
								
								public/styles.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								public/styles.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | |||||||
|  | :root { | ||||||
|  | 	--pico-font-family: "Atkinson Hyperlegible", sans-serif; | ||||||
|  | 	--icd-rentgen-color: #99ffdd; | ||||||
|  | 	--success-background-color: hsl(120, 100%, 85.1%); | ||||||
|  | 	--success-color: hsl(120, 100%, 7.1%); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | section { | ||||||
|  | 	max-width: 800px; | ||||||
|  | 	margin: 0 auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | button { | ||||||
|  | 	width: 100%; | ||||||
|  | 	font-weight: bold !important; | ||||||
|  | 	border: none; | ||||||
|  | 	box-shadow: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #submit { | ||||||
|  | 	background-color: black; | ||||||
|  | 	color: var(--icd-rentgen-color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #submit:hover { | ||||||
|  | 	background-color: var(--icd-rentgen-color); | ||||||
|  | 	color: black; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #submit, | ||||||
|  | #submit:hover { | ||||||
|  | 	border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .hidden { | ||||||
|  | 	display: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .success { | ||||||
|  | 	background-color: var(--success-background-color); | ||||||
|  | 	color: var(--success-color); | ||||||
|  | 	padding: 2rem; | ||||||
|  | 	margin: 1rem; | ||||||
|  | 	font-size: 1.2rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | header, | ||||||
|  | footer { | ||||||
|  | 	max-width: unset !important; | ||||||
|  | 	background-color: black; | ||||||
|  | 	text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | header section h1, | ||||||
|  | footer section p { | ||||||
|  | 	color: white; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | header section h1 { | ||||||
|  | 	margin: 1rem 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | footer section p a { | ||||||
|  | 	color: var(--icd-rentgen-color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | body { | ||||||
|  | 	min-height: 100vh; | ||||||
|  | 	display: flex; | ||||||
|  | 	flex-direction: column; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main { | ||||||
|  | 	flex: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #icd-logo { | ||||||
|  | 	height: 48px; | ||||||
|  | 	aspect-ratio: 1200/319; | ||||||
|  | 	background-color: white; | ||||||
|  | 	mask-image: url("logo.png"); | ||||||
|  | 	mask-repeat: no-repeat; | ||||||
|  | 	mask-size: contain; | ||||||
|  | 	mask-position: center; | ||||||
|  | 	-webkit-mask-image: url("logo.png"); | ||||||
|  | 	-webkit-mask-repeat: no-repeat; | ||||||
|  | 	-webkit-mask-size: contain; | ||||||
|  | 	-webkit-mask-position: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | figure#icd-logo-container { | ||||||
|  | 	background-color: black; | ||||||
|  | 	padding: 1rem; | ||||||
|  | 	width: fit-content; | ||||||
|  | 	margin: 0 auto 1em auto; | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| import { convert } from "."; | import { convert } from "."; | ||||||
|  | import "@fontsource/atkinson-hyperlegible"; | ||||||
| 
 | 
 | ||||||
| function download(data: string, filename: string, type: string) { | function download(data: string, filename: string, type: string) { | ||||||
| 	var file = new Blob([data], { type: type }); | 	var file = new Blob([data], { type: type }); | ||||||
| @ -30,9 +31,7 @@ async function handle() { | |||||||
| 		const result = convert(string); | 		const result = convert(string); | ||||||
| 		download( | 		download( | ||||||
| 			result.output, | 			result.output, | ||||||
| 			`raport-${result.range.date_start.getFullYear()}-${( | 			`raport-${result.range.date_start.getFullYear()}-${(result.range.date_start.getMonth() + 1) | ||||||
| 				result.range.date_start.getMonth() + 1 |  | ||||||
| 			) |  | ||||||
| 				.toString() | 				.toString() | ||||||
| 				.padStart(2, "0")}.mt940`,
 | 				.padStart(2, "0")}.mt940`,
 | ||||||
| 			"text" | 			"text" | ||||||
|  | |||||||
| @ -7,47 +7,31 @@ import * as Diff from "diff"; | |||||||
| 
 | 
 | ||||||
| describe("mt940 converter", () => { | describe("mt940 converter", () => { | ||||||
| 	it("converts properly", async () => { | 	it("converts properly", async () => { | ||||||
| 		const content = await fs.readFile( | 		const content = await fs.readFile(path.resolve(__dirname, "../tests/real_csv.csv"), { | ||||||
| 			path.resolve(__dirname, "../tests/real_csv.csv"), | 			encoding: null, | ||||||
| 			{ | 		}); | ||||||
| 				encoding: null, |  | ||||||
| 			} |  | ||||||
| 		); |  | ||||||
| 		const result = convert(iconv.decode(content, "cp1250")); | 		const result = convert(iconv.decode(content, "cp1250")); | ||||||
| 		const expected_result = await fs.readFile( | 		const expected_result = await fs.readFile(path.resolve(__dirname, "../tests/real_mt940.txt"), "utf-8"); | ||||||
| 			path.resolve(__dirname, "../tests/real_mt940.txt"), |  | ||||||
| 			"utf-8" |  | ||||||
| 		); |  | ||||||
| 		try { | 		try { | ||||||
| 			assert.strictEqual(result.output, expected_result); | 			assert.strictEqual(result.output, expected_result); | ||||||
| 		} catch (e) { | 		} catch (e) { | ||||||
| 			console.error("There was a difference. Fixes to apply:"); | 			console.error("There was a difference. Fixes to apply:"); | ||||||
| 			console.log( | 			console.log(Diff.createPatch("mt940", result.output, expected_result)); | ||||||
| 				Diff.createPatch("mt940", result.output, expected_result) |  | ||||||
| 			); |  | ||||||
| 			throw new Error("Texts differ"); | 			throw new Error("Texts differ"); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	it.only("converts properly", async () => { | 	it.only("converts properly", async () => { | ||||||
| 		const content = await fs.readFile( | 		const content = await fs.readFile(path.resolve(__dirname, "../tests/real_csv_2.csv"), { | ||||||
| 			path.resolve(__dirname, "../tests/real_csv_2.csv"), | 			encoding: null, | ||||||
| 			{ | 		}); | ||||||
| 				encoding: null, |  | ||||||
| 			} |  | ||||||
| 		); |  | ||||||
| 		const result = convert(iconv.decode(content, "cp1250")); | 		const result = convert(iconv.decode(content, "cp1250")); | ||||||
| 		const expected_result = await fs.readFile( | 		const expected_result = await fs.readFile(path.resolve(__dirname, "../tests/real_mt940_2.txt"), "utf-8"); | ||||||
| 			path.resolve(__dirname, "../tests/real_mt940_2.txt"), |  | ||||||
| 			"utf-8" |  | ||||||
| 		); |  | ||||||
| 		try { | 		try { | ||||||
| 			assert.strictEqual(result.output, expected_result); | 			assert.strictEqual(result.output, expected_result); | ||||||
| 		} catch (e) { | 		} catch (e) { | ||||||
| 			console.error("There was a difference. Fixes to apply:"); | 			console.error("There was a difference. Fixes to apply:"); | ||||||
| 			console.log( | 			console.log(Diff.createPatch("mt940", result.output, expected_result)); | ||||||
| 				Diff.createPatch("mt940", result.output, expected_result) |  | ||||||
| 			); |  | ||||||
| 			throw new Error("Texts differ"); | 			throw new Error("Texts differ"); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  | |||||||
							
								
								
									
										96
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/index.ts
									
									
									
									
									
								
							| @ -1,9 +1,4 @@ | |||||||
| import { | import { addLineNumbers, chunks, fillWithEmpty, removeRepeatingSpace } from "./utils"; | ||||||
| 	addLineNumbers, |  | ||||||
| 	chunks, |  | ||||||
| 	fillWithEmpty, |  | ||||||
| 	removeRepeatingSpace, |  | ||||||
| } from "./utils"; |  | ||||||
| 
 | 
 | ||||||
| class Account { | class Account { | ||||||
| 	constructor( | 	constructor( | ||||||
| @ -35,18 +30,12 @@ class Transaction { | |||||||
| 	})(); | 	})(); | ||||||
| 
 | 
 | ||||||
| 	formatTitle() { | 	formatTitle() { | ||||||
| 		return addLineNumbers( | 		return addLineNumbers(fillWithEmpty(chunks(this.title.trim(), 27), 9), 20).join("\n"); | ||||||
| 			fillWithEmpty(chunks(this.title.trim(), 27), 9), |  | ||||||
| 			20 |  | ||||||
| 		).join("\n"); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	formatPerson() { | 	formatPerson() { | ||||||
| 		const ret = addLineNumbers( | 		const ret = addLineNumbers( | ||||||
| 			fillWithEmpty( | 			fillWithEmpty(chunks(removeRepeatingSpace(this.person).trim(), 27), 2).slice(0, 2), | ||||||
| 				chunks(removeRepeatingSpace(this.person).trim(), 27), |  | ||||||
| 				2 |  | ||||||
| 			).slice(0, 2), |  | ||||||
| 			32 | 			32 | ||||||
| 		).join("\n"); | 		).join("\n"); | ||||||
| 		return ret; | 		return ret; | ||||||
| @ -55,26 +44,11 @@ class Transaction { | |||||||
| 	toMT940() { | 	toMT940() { | ||||||
| 		// just a bunch of heuristics
 | 		// just a bunch of heuristics
 | ||||||
| 		const prefix_fns = [ | 		const prefix_fns = [ | ||||||
| 			() => | 			() => (this.description.includes("OPŁATA-PRZELEW WEWN.") ? "169" : false), | ||||||
| 				this.description.includes("OPŁATA-PRZELEW WEWN.") | 			() => (this.description.includes("PRZELEW WEWNĘTRZNY PRZY") ? "160" : false), | ||||||
| 					? "169" | 			() => (this.description.includes("OPŁATA-PRZELEW WEWN. DO") ? "755" : false), | ||||||
| 					: false, | 			() => (this.description.includes("PRZELEW ZEWNĘTRZNY WYCH") ? "152" : false), | ||||||
| 			() => | 			() => (this.description.includes("OPŁATA PRZELEW ZEW.DOWO") ? "771" : false), | ||||||
| 				this.description.includes("PRZELEW WEWNĘTRZNY PRZY") |  | ||||||
| 					? "160" |  | ||||||
| 					: false, |  | ||||||
| 			() => |  | ||||||
| 				this.description.includes("OPŁATA-PRZELEW WEWN. DO") |  | ||||||
| 					? "755" |  | ||||||
| 					: false, |  | ||||||
| 			() => |  | ||||||
| 				this.description.includes("PRZELEW ZEWNĘTRZNY WYCH") |  | ||||||
| 					? "152" |  | ||||||
| 					: false, |  | ||||||
| 			() => |  | ||||||
| 				this.description.includes("OPŁATA PRZELEW ZEW.DOWO") |  | ||||||
| 					? "771" |  | ||||||
| 					: false, |  | ||||||
| 			() => (this.amount > 0 ? "150" : false), | 			() => (this.amount > 0 ? "150" : false), | ||||||
| 			() => "169", | 			() => "169", | ||||||
| 		]; | 		]; | ||||||
| @ -86,11 +60,9 @@ class Transaction { | |||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		const result = `:61:${mtDate(this.acc_date)}${mtDate( | 		const result = `:61:${mtDate(this.acc_date)}${mtDate(this.op_date).slice(2)}${ | ||||||
| 			this.op_date | 			this.amount > 0 ? "C" : "D" | ||||||
| 		).slice(2)}${this.amount > 0 ? "C" : "D"}${mtAmount( | 		}${mtAmount(this.amount)}S${prefix}${Transaction.counter.next().value} | ||||||
| 			this.amount |  | ||||||
| 		)}S${prefix}${Transaction.counter.next().value} |  | ||||||
| :86:${prefix} | :86:${prefix} | ||||||
| :86:${prefix}~00B${prefix}${this.description.slice(0, 23)} | :86:${prefix}~00B${prefix}${this.description.slice(0, 23)} | ||||||
| ${this.formatTitle()} | ${this.formatTitle()} | ||||||
| @ -107,10 +79,7 @@ ${this.formatPerson()} | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Range { | class Range { | ||||||
| 	constructor( | 	constructor(public date_start: Date, public date_end: Date) {} | ||||||
| 		public date_start: Date, |  | ||||||
| 		public date_end: Date |  | ||||||
| 	) {} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| abstract class CSVParser { | abstract class CSVParser { | ||||||
| @ -138,10 +107,7 @@ class mBankParser extends CSVParser { | |||||||
| 			this.parseAmount(lines[last - 2][7]), | 			this.parseAmount(lines[last - 2][7]), | ||||||
| 			lines[18][0] | 			lines[18][0] | ||||||
| 		); | 		); | ||||||
| 		const range = new Range( | 		const range = new Range(this.parseDate(lines[14][0]), this.parseDate(lines[14][1])); | ||||||
| 			this.parseDate(lines[14][0]), |  | ||||||
| 			this.parseDate(lines[14][1]) |  | ||||||
| 		); |  | ||||||
| 		const transactions = []; | 		const transactions = []; | ||||||
| 		for (let i = 38; i <= last - 5; i++) { | 		for (let i = 38; i <= last - 5; i++) { | ||||||
| 			const line = lines[i]; | 			const line = lines[i]; | ||||||
| @ -151,29 +117,14 @@ class mBankParser extends CSVParser { | |||||||
| 			} | 			} | ||||||
| 			if (line.length != 9) { | 			if (line.length != 9) { | ||||||
| 				console.log({ line }); | 				console.log({ line }); | ||||||
| 				throw new Error( | 				throw new Error("Wrong amount of columns! maybe a semicolon got stuck in a transaction description?"); | ||||||
| 					"Wrong amount of columns! maybe a semicolon got stuck in a transaction description?" |  | ||||||
| 				); |  | ||||||
| 			} | 			} | ||||||
| 			const date_acc = new Date(line[0]); | 			const date_acc = new Date(line[0]); | ||||||
| 			const date_op = new Date(line[1]); | 			const date_op = new Date(line[1]); | ||||||
| 			const [description, title, person, account_number] = line | 			const [description, title, person, account_number] = line.slice(2).map(this.trimString); | ||||||
| 				.slice(2) | 			const [amount, balance_after] = line.slice(6).map((s) => this.parseAmount(s)); | ||||||
| 				.map(this.trimString); |  | ||||||
| 			const [amount, balance_after] = line |  | ||||||
| 				.slice(6) |  | ||||||
| 				.map((s) => this.parseAmount(s)); |  | ||||||
| 			transactions.push( | 			transactions.push( | ||||||
| 				new Transaction( | 				new Transaction(date_acc, date_op, description, title, person, account_number, amount, balance_after) | ||||||
| 					date_acc, |  | ||||||
| 					date_op, |  | ||||||
| 					description, |  | ||||||
| 					title, |  | ||||||
| 					person, |  | ||||||
| 					account_number, |  | ||||||
| 					amount, |  | ||||||
| 					balance_after |  | ||||||
| 				) |  | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
| 		return { account, range, transactions }; | 		return { account, range, transactions }; | ||||||
| @ -203,9 +154,10 @@ class mBankParser extends CSVParser { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function mtDate(d: Date) { | function mtDate(d: Date) { | ||||||
| 	return `${d.getFullYear() - 2000}${(d.getMonth() + 1) | 	return `${d.getFullYear() - 2000}${(d.getMonth() + 1).toString().padStart(2, "0")}${d | ||||||
|  | 		.getDate() | ||||||
| 		.toString() | 		.toString() | ||||||
| 		.padStart(2, "0")}${d.getDate().toString().padStart(2, "0")}`;
 | 		.padStart(2, "0")}`;
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function mtAmount(n: number) { | function mtAmount(n: number) { | ||||||
| @ -218,12 +170,8 @@ export function convert(csv_utf8: string): { output: string; range: Range } { | |||||||
| 	const string = `:20:MT940
 | 	const string = `:20:MT940
 | ||||||
| :25:/PL${account.number.replaceAll(/[^0-9]/g, "")} | :25:/PL${account.number.replaceAll(/[^0-9]/g, "")} | ||||||
| :28C:${mtDate(range.date_start)} | :28C:${mtDate(range.date_start)} | ||||||
| :60F:D${mtDate(range.date_start)}${account.currency}${mtAmount( | :60F:D${mtDate(range.date_start)}${account.currency}${mtAmount(account.initial_balance)} | ||||||
| 		account.initial_balance |  | ||||||
| 	)} |  | ||||||
| ${transactions.map((t) => t.toMT940()).join("\n")} | ${transactions.map((t) => t.toMT940()).join("\n")} | ||||||
| :62F:D${mtDate(range.date_end)}${account.currency}${mtAmount( | :62F:D${mtDate(range.date_end)}${account.currency}${mtAmount(account.closing_balance)}`;
 | ||||||
| 		account.closing_balance |  | ||||||
| 	)}`;
 |  | ||||||
| 	return { output: string, range }; | 	return { output: string, range }; | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,11 +15,7 @@ export function fillWithEmpty(array: string[], target_length: number) { | |||||||
| 	return a; | 	return a; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function addLineNumbers( | export function addLineNumbers(s: string[], start_num: number, prefix = "~"): string[] { | ||||||
| 	s: string[], |  | ||||||
| 	start_num: number, |  | ||||||
| 	prefix = "~" |  | ||||||
| ): string[] { |  | ||||||
| 	return s.map((l, i) => `${prefix}${start_num + i}${l}`); | 	return s.map((l, i) => `${prefix}${start_num + i}${l}`); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user