Implement scaffolding of parsers and ING Bank Śląski parser
This commit is contained in:
35
src/parser/base.ts
Normal file
35
src/parser/base.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Transaction } from "@/types/transaction";
|
||||||
|
import { ProfileConfig, ParserConfig } from "@/types/config";
|
||||||
|
|
||||||
|
export abstract class BaseTransactionParser<C extends ParserConfig> {
|
||||||
|
public readonly name: string;
|
||||||
|
protected abstract requiredFields: readonly (keyof C)[];
|
||||||
|
abstract parse(config: C, data: string[]): Promise<Transaction|undefined>;
|
||||||
|
|
||||||
|
constructor(name: string) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#validate(config: Partial<C>|undefined): C {
|
||||||
|
for (const field of this.requiredFields) {
|
||||||
|
if (config?.[field] === undefined) {
|
||||||
|
throw new Error(`The '${String(field)}' configuration field of '${this.name}' parser is required`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config as C;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async parseTransaction(config: ProfileConfig, data: string[]): Promise<Transaction|undefined> {
|
||||||
|
const cfg = config?.config as Partial<C> | undefined;
|
||||||
|
return this.parse(this.#validate(cfg), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected parseAmount(input?: string): number|undefined {
|
||||||
|
if (input === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number.parseFloat(input.replaceAll(",", "."));
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/parser/index.ts
Normal file
24
src/parser/index.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export { BaseTransactionParser } from "./base";
|
||||||
|
import { ProfileConfig, ParserConfig } from "@/types/config";
|
||||||
|
import { BaseTransactionParser } from "./base";
|
||||||
|
import { default as PlIng } from "./pl/ing";
|
||||||
|
import { Transaction } from "@/types/transaction";
|
||||||
|
|
||||||
|
type Constructor<T extends BaseTransactionParser<ParserConfig>> = new (name: string) => T;
|
||||||
|
|
||||||
|
const PARSERS: Record<string, Constructor<BaseTransactionParser<ParserConfig>>> = {
|
||||||
|
"pl.ing": PlIng
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function parseTransaction(parserName: string, config: ProfileConfig, data: string[]): Promise<Transaction|undefined> {
|
||||||
|
const Parser = PARSERS[parserName];
|
||||||
|
|
||||||
|
if (!Parser) {
|
||||||
|
throw new Error(`Unknown parser: ${parserName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new Parser(parserName);
|
||||||
|
|
||||||
|
return parser.parseTransaction(config, data);
|
||||||
|
}
|
||||||
|
|
||||||
64
src/parser/pl/ing/index.ts
Normal file
64
src/parser/pl/ing/index.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { Transaction } from "@/types/transaction";
|
||||||
|
import { ParserConfig } from "@/types/config";
|
||||||
|
import { BaseTransactionParser } from "../..";
|
||||||
|
import { Parser } from "papaparse";
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
'transactionDate',
|
||||||
|
'accountingDate',
|
||||||
|
'contrahentData',
|
||||||
|
'title',
|
||||||
|
'accountNumber',
|
||||||
|
'bankName',
|
||||||
|
'details',
|
||||||
|
'transactionNumber',
|
||||||
|
'transactionAmount',
|
||||||
|
'transactionCurrency',
|
||||||
|
'lockAmount',
|
||||||
|
'lockCurrency',
|
||||||
|
'foreignAmount',
|
||||||
|
'foreignCurrency',
|
||||||
|
'account',
|
||||||
|
'bilanceAfterTransaction',
|
||||||
|
'accountCurrency',
|
||||||
|
'unknown1',
|
||||||
|
'unknown2',
|
||||||
|
'unknown3',
|
||||||
|
'unknown4',
|
||||||
|
];
|
||||||
|
|
||||||
|
type IngTransaction = {
|
||||||
|
[K in typeof headers[number]]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readIngTransaction = (data: string[]): IngTransaction|undefined => {
|
||||||
|
if (data.length !== headers.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transaction: IngTransaction = {};
|
||||||
|
headers.forEach((key, index) => {
|
||||||
|
transaction[key] = data[index];
|
||||||
|
});
|
||||||
|
|
||||||
|
return transaction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class extends BaseTransactionParser<ParserConfig> {
|
||||||
|
protected requiredFields = [];
|
||||||
|
|
||||||
|
async parse(config: ParserConfig, data: string[]): Promise<Transaction | undefined> {
|
||||||
|
const ing = readIngTransaction(data);
|
||||||
|
|
||||||
|
if (!ing) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
account: "TODO: unknown account (not supported yet)",
|
||||||
|
date: ing.transactionDate,
|
||||||
|
amount: this.parseAmount(ing.transactionAmount),
|
||||||
|
imported_payee: ing.contrahentData?.trim()?.replaceAll(/\s+/g, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user