actual-moneymoney
Version:
An importer for syncing MoneyMoney accounts and transactions to Actual.
101 lines (100 loc) • 5.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { getAccounts } from 'moneymoney';
export class AccountMap {
constructor(budgetConfig, logger, actualApi) {
this.budgetConfig = budgetConfig;
this.logger = logger;
this.actualApi = actualApi;
}
getMap(moneyMoneyAccountRefs) {
if (!moneyMoneyAccountRefs)
return this.mapping;
const customMap = new Map();
for (const ref of moneyMoneyAccountRefs) {
const monMonAccount = this.getMoneyMoneyAccountByRef(ref);
if (!monMonAccount) {
this.logger.error(`Specified account ref '${ref}' did not resolve to any MoneyMoney accounts.`);
continue;
}
const actualAccount = this.mapping.get(monMonAccount);
if (!actualAccount) {
this.logger.error(`Could not find an Actual account for specified MoneyMoney account with ref '${ref}'.`);
continue;
}
customMap.set(monMonAccount, actualAccount);
}
return customMap;
}
checkMoneyMoneyAccountRef(account, ref) {
return (account.uuid === ref ||
account.accountNumber === ref ||
account.name === ref);
}
getMoneyMoneyAccountByRef(ref) {
const matchingAccounts = this.moneyMoneyAccounts.filter((acc) => this.checkMoneyMoneyAccountRef(acc, ref));
if (matchingAccounts.length === 0) {
this.logger.warn(`No MoneyMoney account found for reference '${ref}'.`);
return null;
}
else if (matchingAccounts.length > 1) {
this.logger.warn(`Found multiple MoneyMoney accounts matching the reference '${ref}'. Using the first one.`);
}
return matchingAccounts[0];
}
checkActualAccountRef(account, ref) {
return account.id === ref || account.name === ref;
}
getActualAccountByRef(ref) {
const matchingAccounts = this.actualAccounts.filter((acc) => this.checkActualAccountRef(acc, ref));
if (matchingAccounts.length === 0) {
this.logger.warn(`No Actual account found for reference '${ref}'.`);
return null;
}
else if (matchingAccounts.length > 1) {
this.logger.warn(`Found multiple Actual accounts matching the reference '${ref}'. Using the first one.`);
}
return matchingAccounts[0];
}
loadFromConfig() {
return __awaiter(this, void 0, void 0, function* () {
if (this.mapping) {
this.logger.debug('Account mapping already loaded. Skipping re-load...');
return;
}
const accountMapping = this.budgetConfig.accountMapping;
const parsedAccountMapping = new Map();
this.moneyMoneyAccounts = yield getAccounts();
this.logger.debug(`Found ${this.moneyMoneyAccounts.length} accounts in MoneyMoney.`);
this.actualAccounts = yield this.actualApi.getAccounts();
this.logger.debug(`Found ${this.actualAccounts.length} accounts in Actual.`);
this.logger.debug(`Account mapping contains ${Object.entries(accountMapping).length} entries.`);
for (const [moneyMoneyRef, actualRef] of Object.entries(accountMapping)) {
const moneyMoneyAccount = this.getMoneyMoneyAccountByRef(moneyMoneyRef);
const actualAccount = this.getActualAccountByRef(actualRef);
if (!actualAccount) {
this.logger.debug(`No Actual account found for reference '${actualRef}'. Skipping...`);
continue;
}
else if (!moneyMoneyAccount) {
this.logger.debug(`No MoneyMoney account found for reference '${moneyMoneyRef}'. Skipping...`);
continue;
}
this.logger.debug(`MoneyMoney account '${moneyMoneyAccount.name}' will import to Actual account '${actualAccount.name}'.`);
parsedAccountMapping.set(moneyMoneyAccount, actualAccount);
}
this.logger.info('Parsed account mapping', [
'[MoneyMoney Account] → [Actual Account]',
...Array.from(parsedAccountMapping.entries()).map(([monMonAccount, actualAccount]) => `${monMonAccount.name} → ${actualAccount.name}`),
]);
this.mapping = parsedAccountMapping;
});
}
}