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