Files
AtreidesAssetsFree/server/data/accounts.js
T

117 lines
5.8 KiB
JavaScript
Raw Permalink Normal View History

2026-10-29 11:57:29 +01:00
// Generated, do not edit. sha256:defdf4c970fac7940f2bd495e2911283709af51809bb6a2d4c4f9c43ed032b8f
import * as db from '../db.js';
import { toSearchName } from '../../shared/search-name.js';
import { nowUtcIso } from '../../shared/time.js';
import { ENUMS } from '../../shared/config.js';
import { assertEnum, assertName, assertTaxCategory, assertTaxScope, assertWealthScope } from './_shared.js';
import { capacityReachedFor } from './_limits.js';
import { fail } from '../../shared/app-error.js';
import { purgeWalletSourceSecret } from './wallet-sources.js';
import { accountPositions } from './positions.js';
import { trackUpdate, trackDelete } from './change-log.js';
export function listAccounts() {
return db.all(`SELECT * FROM Account ORDER BY name`);
}
export function listSelectableAccounts({ metGesloten = false, ookId = null } = {}) {
return metGesloten
? db.all(`SELECT * FROM Account ORDER BY isActive DESC, name`)
: db.all(`SELECT * FROM Account WHERE isActive = 1 OR id = ?1 ORDER BY isActive DESC, name`,
[ookId ?? null]);
}
export async function createAccount({ name, kind, isOwnCustody, scanEnabled, taxCategory, taxScope, wealthScope }) {
assertEnum('kind', ENUMS.ACCOUNT_KIND, kind);
if (kind === 'bank') {
const max = capacityReachedFor('fiatAccounts');
if (max !== null) {
fail('accounts.maxFreeBank', { max },
`The free version holds ${max} fiat account(s). A licence lifts that; nothing is removed in the meantime.`);
}
}
const naam = assertName('Accountnaam', name);
const categorie = assertTaxCategory(taxCategory);
const scope = assertTaxScope(taxScope);
const wealth = assertWealthScope(wealthScope);
db.run(`INSERT INTO Account (name, searchName, kind, isOwnCustody, scanEnabled, isActive, closedAt, taxCategory, taxScope, wealthScope)
VALUES (?,?,?,?,?,1,NULL,?,?,?)`,
[naam, toSearchName(naam), kind, isOwnCustody ? 1 : 0, scanEnabled ? 1 : 0, categorie, scope, wealth]);
const id = db.lastId();
await db.persist();
return id;
}
export async function updateAccount(id, a) {
assertEnum('kind', ENUMS.ACCOUNT_KIND, a.kind);
const naam = assertName('Accountnaam', a.name);
const categorie = assertTaxCategory(a.taxCategory);
const scopeGiven = Object.hasOwn(a, 'taxScope');
const scope = scopeGiven ? assertTaxScope(a.taxScope) : null;
const wealthGiven = Object.hasOwn(a, 'wealthScope');
const wealth = wealthGiven ? assertWealthScope(a.wealthScope) : null;
trackUpdate('Account', id, () =>
db.run(`UPDATE Account SET name=?, searchName=?, kind=?, isOwnCustody=?, scanEnabled=?, taxCategory=?,
taxScope = CASE WHEN ? THEN ? ELSE taxScope END,
wealthScope = CASE WHEN ? THEN ? ELSE wealthScope END WHERE id=?`,
[naam, toSearchName(naam), a.kind, a.isOwnCustody ? 1 : 0, a.scanEnabled ? 1 : 0, categorie,
scopeGiven ? 1 : 0, scope, wealthGiven ? 1 : 0, wealth, id]));
await db.persist();
}
export async function deleteAccount(id) {
const used = db.one(`SELECT (SELECT COUNT(*) FROM Event WHERE fromAccountId=?1 OR toAccountId=?1)
+ (SELECT COUNT(*) FROM Valuation WHERE accountId =?1)
+ (SELECT COUNT(*) FROM ScanLog WHERE accountId=?1)
+ (SELECT COUNT(*) FROM ReconcileProposal WHERE accountId=?1) AS n`, [id]).n;
if (used > 0) fail('accounts.inUse', { n: used }, `Account is used by ${used} record(s); remove or relink those first.`);
for (const s of db.all(`SELECT id FROM WalletSource WHERE accountId=? AND kind='nwc'`, [id])) {
await purgeWalletSourceSecret(s.id);
}
trackDelete('Account', id, () => db.run(`DELETE FROM Account WHERE id=?`, [id]));
await db.persist();
}
export function deactivateCheck(accountId) {
const account = db.one(`SELECT * FROM Account WHERE id = ?`, [accountId]);
if (!account) return { found: false, findings: [] };
const n = (sql, params = [accountId]) => db.one(sql, params).n;
const findings = [];
if (account.scanEnabled) {
findings.push({ kind: 'scanEnabled', count: 1 });
}
const sources = n(`SELECT COUNT(*) AS n FROM WalletSource WHERE accountId = ?`);
if (sources) findings.push({ kind: 'walletSources', count: sources });
const vandaag = new Date().toISOString().slice(0, 10);
const schedules = n(
`SELECT COUNT(*) AS n FROM InterestSchedule
WHERE accountId = ? AND (endsOn IS NULL OR endsOn >= ?)`, [accountId, vandaag]);
if (schedules) findings.push({ kind: 'openSchedules', count: schedules });
const proposals = n(`SELECT COUNT(*) AS n FROM ReconcileProposal WHERE accountId = ? AND status = 'pending'`);
if (proposals) findings.push({ kind: 'pendingProposals', count: proposals });
const balances = accountPositions(accountId);
if (balances.length) findings.push({ kind: 'balance', count: balances.length, balances });
const events = n(`SELECT COUNT(*) AS n FROM Event WHERE fromAccountId = ?1 OR toAccountId = ?1`);
if (events) findings.push({ kind: 'events', count: events, informational: true });
return { found: true, isActive: !!account.isActive, name: account.name, findings };
}
export async function deactivateAccount(id, closedAt = nowUtcIso()) {
const account = db.one(`SELECT isActive FROM Account WHERE id = ?`, [id]);
if (!account) fail('accounts.unknownAccount', { id }, `Account #${id} does not exist.`);
trackUpdate('Account', id, () =>
db.run(`UPDATE Account SET isActive = 0, closedAt = ?, scanEnabled = 0 WHERE id = ?`, [closedAt, id]));
await db.persist();
}
export async function reactivateAccount(id) {
const account = db.one(`SELECT isActive FROM Account WHERE id = ?`, [id]);
if (!account) fail('accounts.unknownAccount', { id }, `Account #${id} does not exist.`);
trackUpdate('Account', id, () =>
db.run(`UPDATE Account SET isActive = 1, closedAt = NULL WHERE id = ?`, [id]));
await db.persist();
}