Add support for custom transfers in pl.ing
This commit is contained in:
@@ -53,7 +53,7 @@ export class Actual {
|
|||||||
date: transaction.date,
|
date: transaction.date,
|
||||||
amount: utils.amountToInteger(transaction.amount),
|
amount: utils.amountToInteger(transaction.amount),
|
||||||
notes: transaction.title,
|
notes: transaction.title,
|
||||||
imported_payee: transaction.to,
|
imported_payee: transaction.toDetails,
|
||||||
cleared: false
|
cleared: false
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,8 +129,21 @@ export class Actual {
|
|||||||
const output: ActualImportResult = { added: [], updated: [], errors: [] };
|
const output: ActualImportResult = { added: [], updated: [], errors: [] };
|
||||||
|
|
||||||
|
|
||||||
if (this.#dryRun) for (const transaction of toImport ){
|
if (this.#dryRun) {
|
||||||
console.log(`${transaction.imported_payee} ::: ${transaction.notes} ::: ${transaction.amount}`);
|
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) {
|
else for (const transaction of toImport) {
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
|||||||
return {
|
return {
|
||||||
kind: 'regular',
|
kind: 'regular',
|
||||||
from: transaction.accountName,
|
from: transaction.accountName,
|
||||||
|
fromDetails: transaction.accountName,
|
||||||
to,
|
to,
|
||||||
|
toDetails: to,
|
||||||
date: transaction.posted,
|
date: transaction.posted,
|
||||||
title: `${transaction.mCCDescription} (${transaction.originalAmount} ${transaction.currencyDesc}, conv. rate: ${transaction.conversionRate})`,
|
title: `${transaction.mCCDescription} (${transaction.originalAmount} ${transaction.currencyDesc}, conv. rate: ${transaction.conversionRate})`,
|
||||||
amount: -amount,
|
amount: -amount,
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
import { Transaction } from "@/types/transaction";
|
import { Transaction } from "@/types/transaction";
|
||||||
import { ParserConfig } from "@/types/config";
|
import { ParserConfig, ProfileConfig, ServerConfig } from "@/types/config";
|
||||||
import { BaseTransactionParser } from "../..";
|
import { BaseTransactionParser } from "../..";
|
||||||
import { mapCombine, parseAmount } from "@/util/parser";
|
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 = {
|
type IngTransaction = {
|
||||||
[K in typeof headers[number]]: string;
|
[K in typeof headers[number]]: string;
|
||||||
@@ -36,9 +50,26 @@ const headers = [
|
|||||||
'unknown4',
|
'unknown4',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default class extends BaseTransactionParser<ParserConfig> {
|
export default class extends BaseTransactionParser<IngParserConfig> {
|
||||||
protected requiredFields = [];
|
protected requiredFields = [];
|
||||||
#transactions: ValidatedIngTransaction[] = [];
|
#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> {
|
async pushTransaction(data: string[]): Promise<boolean> {
|
||||||
if (data.length !== headers.length) {
|
if (data.length !== headers.length) {
|
||||||
@@ -76,8 +107,24 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reconcile(): Promise<Transaction[]> {
|
async reconcile(): Promise<Transaction[]> {
|
||||||
const transactions = mapCombine(this.#transactions, "transactionNumber", this.#map, this.#combine);
|
return mapCombine(this.#transactions, "transactionNumber", this.#map, this.#combine)
|
||||||
return transactions.reverse();
|
.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 {
|
#map(transaction: ValidatedIngTransaction): Transaction {
|
||||||
@@ -90,7 +137,9 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
|||||||
return {
|
return {
|
||||||
kind: 'regular',
|
kind: 'regular',
|
||||||
from: transaction.account,
|
from: transaction.account,
|
||||||
|
fromDetails: transaction.account,
|
||||||
to: transaction.contrahentData,
|
to: transaction.contrahentData,
|
||||||
|
toDetails: transaction.contrahentData,
|
||||||
date: transaction.transactionDate,
|
date: transaction.transactionDate,
|
||||||
title: transaction.title,
|
title: transaction.title,
|
||||||
id: transaction.transactionNumber,
|
id: transaction.transactionNumber,
|
||||||
@@ -130,14 +179,18 @@ export default class extends BaseTransactionParser<ParserConfig> {
|
|||||||
// Transaction B => A
|
// Transaction B => A
|
||||||
if (aAmount > 0) {
|
if (aAmount > 0) {
|
||||||
transaction.from = b.account;
|
transaction.from = b.account;
|
||||||
|
transaction.fromDetails = b.account;
|
||||||
transaction.to = a.account;
|
transaction.to = a.account;
|
||||||
|
transaction.toDetails = a.account;
|
||||||
transaction.amount = aAmount;
|
transaction.amount = aAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction A => B
|
// Transaction A => B
|
||||||
else {
|
else {
|
||||||
transaction.from = a.account;
|
transaction.from = a.account;
|
||||||
|
transaction.fromDetails = a.account;
|
||||||
transaction.to = b.account;
|
transaction.to = b.account;
|
||||||
|
transaction.toDetails = b.account;
|
||||||
transaction.amount = bAmount;
|
transaction.amount = bAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ export type Transaction = {
|
|||||||
date: string;
|
date: string;
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
|
fromDetails: string;
|
||||||
|
toDetails: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
id?: string;
|
id?: string;
|
||||||
title?: 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 {
|
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 ctx = { ...standardContext, ...context };
|
||||||
const filter = new Function(i1, i2, ...Object.keys(ctx), code);
|
const fn = new Function(i1, i2, ...Object.keys(ctx), code);
|
||||||
return (i1: I1, i2: I2) => filter(i1, i2, ...Object.values(ctx));
|
return (i1: I1, i2: I2) => fn(i1, i2, ...Object.values(ctx));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user