Add simple support for SGB Oława
This commit is contained in:
@@ -33,4 +33,12 @@ export abstract class BaseTransactionParser<C extends ParserConfig> {
|
|||||||
abstract pushTransaction(data: string[]): Promise<boolean>;
|
abstract pushTransaction(data: string[]): Promise<boolean>;
|
||||||
|
|
||||||
abstract reconcile(): Promise<Transaction[]>;
|
abstract reconcile(): Promise<Transaction[]>;
|
||||||
|
|
||||||
|
get csvConfig(): Record<string, unknown>|undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get csvEncoding(): string {
|
||||||
|
return "utf8";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,14 @@ import { ProfileConfig, ParserConfig, ServerConfig } from "@/types/config";
|
|||||||
import { BaseTransactionParser } from "./base";
|
import { BaseTransactionParser } from "./base";
|
||||||
import { default as PlIng } from "./pl/ing";
|
import { default as PlIng } from "./pl/ing";
|
||||||
import { default as PlAmex } from "./pl/amex";
|
import { default as PlAmex } from "./pl/amex";
|
||||||
|
import { default as PlSgbOlawa } from "./pl/sgbolawa";
|
||||||
|
|
||||||
type Constructor<T extends BaseTransactionParser<ParserConfig>> = new (name: string) => T;
|
type Constructor<T extends BaseTransactionParser<ParserConfig>> = new (name: string) => T;
|
||||||
|
|
||||||
const PARSERS: Record<string, Constructor<BaseTransactionParser<ParserConfig>>> = {
|
const PARSERS: Record<string, Constructor<BaseTransactionParser<ParserConfig>>> = {
|
||||||
"pl.ing": PlIng,
|
"pl.ing": PlIng,
|
||||||
"pl.amex": PlAmex,
|
"pl.amex": PlAmex,
|
||||||
|
"pl.sgbolawa": PlSgbOlawa,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createParser(config: ProfileConfig, serverConfig: ServerConfig): BaseTransactionParser<ParserConfig> {
|
export function createParser(config: ProfileConfig, serverConfig: ServerConfig): BaseTransactionParser<ParserConfig> {
|
||||||
|
|||||||
@@ -152,20 +152,14 @@ export default class extends BaseTransactionParser<IngParserConfig> {
|
|||||||
const bAmount = b.transactionAmount ?? b.lockAmount;
|
const bAmount = b.transactionAmount ?? b.lockAmount;
|
||||||
|
|
||||||
if (a.date !== b.date) {
|
if (a.date !== b.date) {
|
||||||
console.log(a);
|
|
||||||
console.log(b);
|
|
||||||
throw new Error(`Transfer transaction dates mismatch`);
|
throw new Error(`Transfer transaction dates mismatch`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.title !== b.title) {
|
if (a.title !== b.title) {
|
||||||
console.log(a);
|
|
||||||
console.log(b);
|
|
||||||
throw new Error(`Transfer transaction titles mismatch`);
|
throw new Error(`Transfer transaction titles mismatch`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aAmount === undefined || bAmount === undefined) {
|
if (aAmount === undefined || bAmount === undefined) {
|
||||||
console.log(a);
|
|
||||||
console.log(b);
|
|
||||||
throw new Error(`Undefined amounts for transactions`);
|
throw new Error(`Undefined amounts for transactions`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,4 +190,8 @@ export default class extends BaseTransactionParser<IngParserConfig> {
|
|||||||
|
|
||||||
return transaction as Transaction;
|
return transaction as Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get csvEncoding(): string {
|
||||||
|
return "win1250";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
103
src/parser/pl/sgbolawa/index.ts
Normal file
103
src/parser/pl/sgbolawa/index.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { BaseTransactionParser } from "@/parser/base";
|
||||||
|
import { ParserConfig } from "@/types/config";
|
||||||
|
import { Transaction } from "@/types/transaction";
|
||||||
|
import { deduplicateSpaces, parseAmount } from "@/util/parser";
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
'Lp',
|
||||||
|
'Tytuł_1',
|
||||||
|
'Tytuł_2',
|
||||||
|
'Tytuł_3',
|
||||||
|
'Tytuł_4',
|
||||||
|
'Rachunek Nadawcy',
|
||||||
|
'Odbiorca_1',
|
||||||
|
'Odbiorca_2',
|
||||||
|
'Odbiorca_3',
|
||||||
|
'Odbiorca_4',
|
||||||
|
'Rachunek Odbiorcy',
|
||||||
|
'Nadawca_1',
|
||||||
|
'Nadawca_2',
|
||||||
|
'Nadawca_3',
|
||||||
|
'Nadawca_4',
|
||||||
|
'Data_operacji',
|
||||||
|
'Kwota',
|
||||||
|
'Saldo',
|
||||||
|
'Nr_dokumentu',
|
||||||
|
'Data_waluty',
|
||||||
|
'Data_nadania',
|
||||||
|
];
|
||||||
|
|
||||||
|
type SgbTransaction = {
|
||||||
|
[K in typeof headers[number]]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ValidatedSgbTransaction = Omit<SgbTransaction, 'Kwota'> & {
|
||||||
|
Kwota: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class extends BaseTransactionParser<ParserConfig> {
|
||||||
|
protected requiredFields = [];
|
||||||
|
#header = true;
|
||||||
|
#transactions: ValidatedSgbTransaction[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
async pushTransaction(data: string[]): Promise<boolean> {
|
||||||
|
if (data.length !== headers.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#header) {
|
||||||
|
this.#header = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transaction: SgbTransaction = {};
|
||||||
|
headers.forEach((key, index) => {
|
||||||
|
transaction[key] = data[index].trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Kwota = parseAmount(transaction.Kwota);
|
||||||
|
|
||||||
|
if (Kwota === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#transactions.push({
|
||||||
|
...transaction,
|
||||||
|
Kwota
|
||||||
|
} as ValidatedSgbTransaction);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async reconcile(): Promise<Transaction[]> {
|
||||||
|
return this.#transactions.map(this.#mapTransaction.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapTransaction(transaction: ValidatedSgbTransaction): Transaction {
|
||||||
|
const from = deduplicateSpaces(`${transaction.Nadawca_1} ${transaction.Nadawca_2} ${transaction.Nadawca_3}`.trim());
|
||||||
|
const to = deduplicateSpaces(`${transaction.Odbiorca_1} ${transaction.Odbiorca_2} ${transaction.Odbiorca_3}`.trim());
|
||||||
|
const title = deduplicateSpaces(`${transaction['Tytuł_1']} ${transaction['Tytuł_2']} ${transaction['Tytuł_3']}`.trim());
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: 'regular',
|
||||||
|
date: transaction.Data_nadania,
|
||||||
|
amount: transaction.Kwota,
|
||||||
|
from,
|
||||||
|
fromDetails: from,
|
||||||
|
to,
|
||||||
|
toDetails: to,
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get csvEncoding(): string {
|
||||||
|
return "win1250";
|
||||||
|
}
|
||||||
|
|
||||||
|
get csvConfig(): Record<string, unknown> {
|
||||||
|
return {
|
||||||
|
delimiter: ";"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,8 +55,8 @@ const submitTransactions = (load: (server: Actual, t: Transaction[]) => Promise<
|
|||||||
};
|
};
|
||||||
|
|
||||||
stream
|
stream
|
||||||
.pipe(iconv.decodeStream(profileConfig.encoding ?? "utf8"))
|
.pipe(iconv.decodeStream(profileConfig.encoding ?? parser.csvEncoding ?? "utf8"))
|
||||||
.pipe(Papa.parse(Papa.NODE_STREAM_INPUT))
|
.pipe(Papa.parse(Papa.NODE_STREAM_INPUT, profileConfig.csv ?? parser.csvConfig))
|
||||||
.on('data', handleRow)
|
.on('data', handleRow)
|
||||||
.on('close', handleClose);
|
.on('close', handleClose);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type ProfileConfig = {
|
|||||||
parser: string;
|
parser: string;
|
||||||
encoding?: string;
|
encoding?: string;
|
||||||
config?: ParserConfig;
|
config?: ParserConfig;
|
||||||
|
csv?: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ParserConfig = {
|
export type ParserConfig = {
|
||||||
|
|||||||
@@ -64,3 +64,7 @@ export function parseAmount(input?: string): number|undefined {
|
|||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deduplicateSpaces(input: string): string {
|
||||||
|
return input.replaceAll(/\s+/, " ");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user