Add support for Amex importer

This commit is contained in:
2025-04-03 18:18:03 +02:00
parent df06c7c15c
commit d5d1218f5c
2 changed files with 97 additions and 1 deletions

View File

@@ -2,11 +2,13 @@ export { BaseTransactionParser } from "./base";
import { ProfileConfig, ParserConfig, ServerConfig } from "@/types/config";
import { BaseTransactionParser } from "./base";
import { default as PlIng } from "./pl/ing";
import { default as PlAmex } from "./pl/amex";
type Constructor<T extends BaseTransactionParser<ParserConfig>> = new (name: string) => T;
const PARSERS: Record<string, Constructor<BaseTransactionParser<ParserConfig>>> = {
"pl.ing": PlIng
"pl.ing": PlIng,
"pl.amex": PlAmex,
};
export function createParser(config: ProfileConfig, serverConfig: ServerConfig): BaseTransactionParser<ParserConfig> {

View File

@@ -0,0 +1,94 @@
import { BaseTransactionParser } from "@/parser/base";
import { ParserConfig } from "@/types/config";
import { Transaction } from "@/types/transaction";
import { parseAmount } from "@/util/parser";
type AmexTransaction = {
[K in typeof headers[number]]: string;
};
type ValidatedAmexTransaction = Omit<AmexTransaction, 'originalAmount' | 'billedAmount'> & {
originalAmount: number;
billedAmount: number;
};
const headers = [
'posted',
'occurred',
'merchantName',
'merchantCity',
'merchantState',
'merchantZipCode',
'mCC/SICCode',
'mCCDescription',
'originalAmount',
'currencyDesc',
'conversionRate',
'billedAmount',
'accountAppliedTo',
'debitCredit',
'referenceNumber',
'statementCycle',
'accountName',
'accountNumber'
];
export default class extends BaseTransactionParser<ParserConfig> {
protected requiredFields = [];
#transactions: ValidatedAmexTransaction[] = [];
async pushTransaction(data: string[]): Promise<boolean> {
if (data.length !== headers.length) {
return false;
}
const transaction: AmexTransaction = {};
headers.forEach((key, index) => {
transaction[key] = data[index].trim();
})
if (!/^\d{4}-\d{2}-\d{2}$/.test(data[0])) {
return false;
}
const originalAmount = parseAmount(transaction.originalAmount);
const billedAmount = parseAmount(transaction.billedAmount);
if (originalAmount === undefined && billedAmount === undefined) {
return false;
}
this.#transactions.push({
...transaction,
originalAmount,
billedAmount
} as ValidatedAmexTransaction);
return true;
}
async reconcile(): Promise<Transaction[]> {
return this.#transactions.map(transaction => {
const amount = transaction.billedAmount;
if (!amount) {
throw new Error(`Undefined amount for transaction: ${transaction}`);
}
const to = [transaction.merchantName, transaction.merchantCity, transaction.merchantState, transaction.merchantZipCode]
.filter(x => x !== undefined)
.map(x => x.trim())
.filter(x => x !== "")
.join(" ");
return {
kind: 'regular',
from: transaction.accountName,
to,
date: transaction.posted,
title: `${transaction.mCCDescription} (${transaction.originalAmount} ${transaction.currencyDesc}, conv. rate: ${transaction.conversionRate})`,
amount: -amount,
};
});
}
}