Differentiate credits and debts

This commit is contained in:
Kuba Orlik 2024-08-05 17:46:50 +02:00
parent 06c08ef93b
commit 9fb888c59d
5 changed files with 125 additions and 12 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ coverage
@types @types
public/*.js public/*.js
/public/bundle.js.map

27
package-lock.json generated
View File

@ -7,6 +7,7 @@
"": { "": {
"name": "mbank-mt940", "name": "mbank-mt940",
"version": "0.1.0", "version": "0.1.0",
"hasInstallScript": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@types/mocha": "^10.0.1", "@types/mocha": "^10.0.1",
@ -20,8 +21,10 @@
}, },
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@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",
"diff": "^5.2.0",
"eslint": "^8.38.0", "eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
@ -1144,6 +1147,13 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"dev": true "dev": true
}, },
"node_modules/@types/diff": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.2.1.tgz",
"integrity": "sha512-uxpcuwWJGhe2AR1g8hD9F5OYGCqjqWnBUQFD8gMZsDbv8oPHzxJF6iMO6n8Tk0AdzlxoaaoQhOYlIg/PukVU8g==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.11", "version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -2190,10 +2200,11 @@
} }
}, },
"node_modules/diff": { "node_modules/diff": {
"version": "5.0.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
"integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
"dev": true, "dev": true,
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.3.1" "node": ">=0.3.1"
} }
@ -3696,6 +3707,16 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"node_modules/mocha/node_modules/diff": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
"integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/mocha/node_modules/js-yaml": { "node_modules/mocha/node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",

View File

@ -19,8 +19,10 @@
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2", "@istanbuljs/nyc-config-typescript": "^1.0.2",
"@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",
"diff": "^5.2.0",
"eslint": "^8.38.0", "eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
@ -40,7 +42,7 @@
"esbuild": "^0.19.9", "esbuild": "^0.19.9",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"yargs": "^17.7.2", "typescript": "^5.0.4",
"typescript": "^5.0.4" "yargs": "^17.7.2"
} }
} }

54
src/index.test.ts Normal file
View File

@ -0,0 +1,54 @@
import { promises as fs } from "fs";
import path from "node:path";
import iconv from "iconv-lite";
import { convert } from ".";
import assert from "node:assert";
import * as Diff from "diff";
describe("mt940 converter", () => {
it("converts properly", async () => {
const content = await fs.readFile(
path.resolve(__dirname, "../tests/real_csv.csv"),
{
encoding: null,
}
);
const result = convert(iconv.decode(content, "cp1250"));
const expected_result = await fs.readFile(
path.resolve(__dirname, "../tests/real_mt940.txt"),
"utf-8"
);
try {
assert.strictEqual(result.output, expected_result);
} catch (e) {
console.error("There was a difference. Fixes to apply:");
console.log(
Diff.createPatch("mt940", result.output, expected_result)
);
throw new Error("Texts differ");
}
});
it.only("converts properly", async () => {
const content = await fs.readFile(
path.resolve(__dirname, "../tests/real_csv_2.csv"),
{
encoding: null,
}
);
const result = convert(iconv.decode(content, "cp1250"));
const expected_result = await fs.readFile(
path.resolve(__dirname, "../tests/real_mt940_2.txt"),
"utf-8"
);
try {
assert.strictEqual(result.output, expected_result);
} catch (e) {
console.error("There was a difference. Fixes to apply:");
console.log(
Diff.createPatch("mt940", result.output, expected_result)
);
throw new Error("Texts differ");
}
});
});

View File

@ -42,21 +42,55 @@ class Transaction {
} }
formatPerson() { formatPerson() {
return addLineNumbers( const ret = addLineNumbers(
fillWithEmpty( fillWithEmpty(
chunks(removeRepeatingSpace(this.person).trim(), 27), chunks(removeRepeatingSpace(this.person).trim(), 27),
2 2
).slice(0, 2), ).slice(0, 2),
32 32
).join("\n"); ).join("\n");
return ret;
} }
toMT940() { toMT940() {
const prefixes = { incoming: "150", outcoming: "169" }; // just a bunch of heuristics
const prefix = this.amount > 0 ? prefixes.incoming : prefixes.outcoming; const prefix_fns = [
return `:61:${mtDate(this.acc_date)}${mtDate(this.op_date).slice( () =>
2 this.description.includes("OPŁATA-PRZELEW WEWN.")
)}C${mtAmount(this.amount)}S${prefix}${Transaction.counter.next().value} ? "169"
: 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),
() => "169",
];
let prefix = "";
for (const fn of prefix_fns) {
const result = fn();
if (result !== false) {
prefix = result;
break;
}
}
const result = `:61:${mtDate(this.acc_date)}${mtDate(
this.op_date
).slice(2)}${this.amount > 0 ? "C" : "D"}${mtAmount(
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()}
@ -68,6 +102,7 @@ ${this.formatPerson()}
~38PL${this.account_number} ~38PL${this.account_number}
~62 ~62
~63`; ~63`;
return result;
} }
} }
@ -166,7 +201,7 @@ function mtDate(d: Date) {
} }
function mtAmount(n: number) { function mtAmount(n: number) {
return Math.abs(n).toString().replace(".", ","); return Math.abs(n).toFixed(2).replace(".", ",");
} }
export function convert(csv_utf8: string): { output: string; range: Range } { export function convert(csv_utf8: string): { output: string; range: Range } {