Add support for custom transfers in pl.ing
This commit is contained in:
@@ -53,7 +53,7 @@ export class Actual {
|
||||
date: transaction.date,
|
||||
amount: utils.amountToInteger(transaction.amount),
|
||||
notes: transaction.title,
|
||||
imported_payee: transaction.to,
|
||||
imported_payee: transaction.toDetails,
|
||||
cleared: false
|
||||
};
|
||||
|
||||
@@ -129,8 +129,21 @@ export class Actual {
|
||||
const output: ActualImportResult = { added: [], updated: [], errors: [] };
|
||||
|
||||
|
||||
if (this.#dryRun) for (const transaction of toImport ){
|
||||
console.log(`${transaction.imported_payee} ::: ${transaction.notes} ::: ${transaction.amount}`);
|
||||
if (this.#dryRun) {
|
||||
const data: { value: (t: ActualTransaction) => string|undefined, header: string, pad: number }[] = [
|
||||
{ value: t => t.payee !== undefined ? "Yes" : "No", header: "Known payee", pad: 12 },
|
||||
{ value: t => t.imported_payee, header: "Imported payee", pad: 70 },
|
||||
{ value: t => t.amount?.toString(), header: "Amount", pad: 10 },
|
||||
{ value: t => t.notes, header: "Notes", pad: 100 }
|
||||
];
|
||||
|
||||
const header = data.map(d => d.header.padEnd(d.pad)).join(" ");
|
||||
const rows = toImport.map(transaction => data.map(d => (d.value(transaction) ?? "").padEnd(d.pad)).join(" "));
|
||||
|
||||
console.log(header);
|
||||
console.log("-".repeat(Math.max(...rows.map(x => x.length)) ?? header.length));
|
||||
rows.forEach(x => console.log(x));
|
||||
|
||||
}
|
||||
|
||||
else for (const transaction of toImport) {
|
||||
|
||||
@@ -84,7 +84,9 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
||||
return {
|
||||
kind: 'regular',
|
||||
from: transaction.accountName,
|
||||
fromDetails: transaction.accountName,
|
||||
to,
|
||||
toDetails: to,
|
||||
date: transaction.posted,
|
||||
title: `${transaction.mCCDescription} (${transaction.originalAmount} ${transaction.currencyDesc}, conv. rate: ${transaction.conversionRate})`,
|
||||
amount: -amount,
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import { Transaction } from "@/types/transaction";
|
||||
import { ParserConfig } from "@/types/config";
|
||||
import { ParserConfig, ProfileConfig, ServerConfig } from "@/types/config";
|
||||
import { BaseTransactionParser } from "../..";
|
||||
import { mapCombine, parseAmount } from "@/util/parser";
|
||||
import { js1 } from "@/util/code";
|
||||
|
||||
type CustomTransferDefinition = {
|
||||
to?: string;
|
||||
when?: string;
|
||||
};
|
||||
|
||||
type CustomTransfer = Required<CustomTransferDefinition> & {
|
||||
fn: (transaction: Transaction) => boolean;
|
||||
};
|
||||
|
||||
type IngParserConfig = ParserConfig & {
|
||||
transfers?: CustomTransferDefinition[];
|
||||
};
|
||||
|
||||
type IngTransaction = {
|
||||
[K in typeof headers[number]]: string;
|
||||
@@ -36,9 +50,26 @@ const headers = [
|
||||
'unknown4',
|
||||
];
|
||||
|
||||
export default class extends BaseTransactionParser<ParserConfig> {
|
||||
export default class extends BaseTransactionParser<IngParserConfig> {
|
||||
protected requiredFields = [];
|
||||
#transactions: ValidatedIngTransaction[] = [];
|
||||
#transfers: CustomTransfer[] = [];
|
||||
|
||||
protected configure(config: Omit<ProfileConfig, "config"> & { config: IngParserConfig }, serverConfig: ServerConfig): void {
|
||||
this.#transfers = config.config.transfers?.map(t => {
|
||||
if (!t.to) {
|
||||
throw new Error(`Undefined 'to' attribute for pl.ing transfer: ${JSON.stringify(t)}`);
|
||||
}
|
||||
|
||||
if (!t.when) {
|
||||
throw new Error(`Undefined 'when' condition for pl.ing transfer: ${JSON.stringify(t)}`);
|
||||
}
|
||||
|
||||
const fn = js1<Transaction, boolean>(t.when);
|
||||
|
||||
return ({ ...t, fn }) as CustomTransfer;
|
||||
}) ?? [];
|
||||
}
|
||||
|
||||
async pushTransaction(data: string[]): Promise<boolean> {
|
||||
if (data.length !== headers.length) {
|
||||
@@ -76,8 +107,24 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
||||
}
|
||||
|
||||
async reconcile(): Promise<Transaction[]> {
|
||||
const transactions = mapCombine(this.#transactions, "transactionNumber", this.#map, this.#combine);
|
||||
return transactions.reverse();
|
||||
return mapCombine(this.#transactions, "transactionNumber", this.#map, this.#combine)
|
||||
.map(this.#mapCustomTransfers.bind(this))
|
||||
.reverse();
|
||||
}
|
||||
|
||||
#mapCustomTransfers(transaction: Transaction): Transaction {
|
||||
const transfer = this.#transfers.find(transfer => transfer.fn(transaction));
|
||||
|
||||
if (transfer) {
|
||||
return {
|
||||
...transaction,
|
||||
kind: 'transfer',
|
||||
to: transfer.to,
|
||||
amount: -transaction.amount,
|
||||
};
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
#map(transaction: ValidatedIngTransaction): Transaction {
|
||||
@@ -90,7 +137,9 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
||||
return {
|
||||
kind: 'regular',
|
||||
from: transaction.account,
|
||||
fromDetails: transaction.account,
|
||||
to: transaction.contrahentData,
|
||||
toDetails: transaction.contrahentData,
|
||||
date: transaction.transactionDate,
|
||||
title: transaction.title,
|
||||
id: transaction.transactionNumber,
|
||||
@@ -130,14 +179,18 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
||||
// Transaction B => A
|
||||
if (aAmount > 0) {
|
||||
transaction.from = b.account;
|
||||
transaction.fromDetails = b.account;
|
||||
transaction.to = a.account;
|
||||
transaction.toDetails = a.account;
|
||||
transaction.amount = aAmount;
|
||||
}
|
||||
|
||||
// Transaction A => B
|
||||
else {
|
||||
transaction.from = a.account;
|
||||
transaction.fromDetails = a.account;
|
||||
transaction.to = b.account;
|
||||
transaction.toDetails = b.account;
|
||||
transaction.amount = bAmount;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ export type Transaction = {
|
||||
date: string;
|
||||
from: string;
|
||||
to: string;
|
||||
fromDetails: string;
|
||||
toDetails: string;
|
||||
amount: number;
|
||||
id?: string;
|
||||
title?: string;
|
||||
|
||||
@@ -2,8 +2,14 @@ const standardContext = {
|
||||
|
||||
};
|
||||
|
||||
export function js1<I, O>(code: string, i1: string = "$", context: Record<string, unknown> = {}): (i: I) => O {
|
||||
const ctx = { ...standardContext, ...context };
|
||||
const fn = new Function(i1, ...Object.keys(ctx), code);
|
||||
return (i: I) => fn(i, ...Object.values(ctx));
|
||||
}
|
||||
|
||||
export function js2<I1, I2, O>(code: string, i1: string, i2: string, context: Record<string, unknown> = {}): (i1: I1, i2: I2) => O {
|
||||
const ctx = { ...standardContext, ...context };
|
||||
const filter = new Function(i1, i2, ...Object.keys(ctx), code);
|
||||
return (i1: I1, i2: I2) => filter(i1, i2, ...Object.values(ctx));
|
||||
const fn = new Function(i1, i2, ...Object.keys(ctx), code);
|
||||
return (i1: I1, i2: I2) => fn(i1, i2, ...Object.values(ctx));
|
||||
}
|
||||
Reference in New Issue
Block a user