// Generated, do not edit. sha256:297c7a1d9ed86e15053cd37bb7126f5fb5b54d94b67e8da901fb66f370ca9301 import * as db from '../db.js'; import { ENUMS } from '../../shared/config.js'; import { nowUtcIso } from '../../shared/time.js'; import { assertEnum } from './_shared.js'; import { capacityReachedFor } from './_limits.js'; import { fail } from '../../shared/app-error.js'; import { eventsCountByAccount, eventsNetByAccount } from './events.js'; import { trackDelete, trackUpdate } from './change-log.js'; import { emitChange } from './_events.js'; export function listValuations() { return db.all(`SELECT v.*, ac.name AS account_name, a.symbol AS asset_symbol, a.name AS asset_name, a.decimals AS asset_decimals, a.unit AS asset_unit, a.subunit AS asset_subunit, a.displayUnit AS asset_display_unit, a.kind AS asset_kind FROM Valuation v LEFT JOIN Account ac ON ac.id = v.accountId JOIN Asset a ON a.id = v.assetId ORDER BY v.valuedOn DESC, v.rateAt DESC, v.id DESC`); } export function anchorAt(accountId, assetId, atIso) { return db.one( `SELECT * FROM Valuation WHERE accountId = ?1 AND assetId = ?2 ${atIso ? 'AND rateAt <= ?3' : ''} ORDER BY rateAt DESC, id DESC LIMIT 1`, atIso ? [accountId, assetId, atIso] : [accountId, assetId]) || null; } export function latestAnchors(atIso, { ownOnly = false } = {}) { return db.all( `SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY accountId, assetId ORDER BY rateAt DESC, id DESC) AS rn FROM Valuation WHERE accountId IS NOT NULL AND (?1 IS NULL OR rateAt <= ?1) AND (?2 = 0 OR accountId IN (SELECT id FROM Account WHERE wealthScope IS NULL)) ) WHERE rn = 1`, [atIso ?? null, ownOnly ? 1 : 0]); } export function anchorsBetween(accountId, assetId, fromIso, untilIso) { return db.all( `SELECT * FROM Valuation WHERE accountId = ?1 AND assetId = ?2 AND rateAt > ?3 AND rateAt <= ?4 ORDER BY rateAt ASC, id ASC`, [accountId, assetId, fromIso, untilIso]); } export function anchorDaysBetween(accountId, assetId, vanDag, totDag) { return db.all( `SELECT DISTINCT valuedOn FROM Valuation WHERE accountId = ?1 AND assetId = ?2 AND valuedOn > ?3 AND valuedOn < ?4 ORDER BY valuedOn`, [accountId, assetId, vanDag, totDag]).map(r => r.valuedOn); } export function firstAnchorDate() { const r = db.one(`SELECT MIN(rateAt) AS first FROM Valuation WHERE accountId IS NOT NULL`); return r && r.first ? r.first : null; } export function valuationDiff(v) { if (!v.accountId) return null; return v.balance - eventsNetByAccount(v.accountId, v.assetId, v.rateAt); } export function anchorBefore(accountId, assetId, atIso) { return db.one( `SELECT * FROM Valuation WHERE accountId = ?1 AND assetId = ?2 AND rateAt < ?3 ORDER BY rateAt DESC, id DESC LIMIT 1`, [accountId, assetId, atIso]) || null; } export function valuationGap(v) { if (!v.accountId) return null; const vorige = anchorBefore(v.accountId, v.assetId, v.rateAt); if (!vorige) return valuationDiff(v); const sindsVorige = eventsNetByAccount(v.accountId, v.assetId, v.rateAt) - eventsNetByAccount(v.accountId, v.assetId, vorige.rateAt); return v.balance - (vorige.balance + sindsVorige); } export function valuationReconcilable(v) { if (!v.accountId) return false; return eventsCountByAccount(v.accountId, v.assetId, v.rateAt) > 0; } function verrijkt() { const nu = nowUtcIso(); return listValuations().map(v => ({ ...v, diff: valuationDiff(v), gap: valuationGap(v), reconcilable: valuationReconcilable(v), accepted: !!v.gapAcceptedAt, future: v.rateAt > nu, })); } export function valuationDiffs() { return verrijkt().map(({ id, diff, gap, reconcilable, accepted, future }) => ({ id, diff, gap, reconcilable, accepted, future })); } export function listOpenValuationDiffs() { return verrijkt().filter(v => !v.future && !v.accepted && v.reconcilable && v.gap != null && v.gap !== 0); } export async function updateValuation(id, v) { assertEnum('source', ENUMS.VALUATION_SOURCE, v.source); trackUpdate('Valuation', id, () => db.run(`UPDATE Valuation SET assetId=?, valuedOn=?, accountId =?, balance=?, fiatRate=?, rateAt=?, source=? WHERE id=?`, [v.assetId, v.valuedOn, v.accountId ?? null, v.balance, v.fiatRate, v.rateAt, v.source, id])); await db.persist(); emitChange('ledger-changed'); } export async function acceptValuationGap(id, aan = true) { trackUpdate('Valuation', id, () => db.run(`UPDATE Valuation SET gapAcceptedAt = ? WHERE id = ?`, [aan ? nowUtcIso() : null, id])); await db.persist(); emitChange('ledger-changed'); } export async function deleteValuation(id) { trackDelete('Valuation', id, () => db.run(`DELETE FROM Valuation WHERE id=?`, [id])); await db.persist(); emitChange('ledger-changed'); } export async function createValuation(v) { assertEnum('source', ENUMS.VALUATION_SOURCE, v.source); const max = capacityReachedFor('valuations'); if (max !== null) { fail('valuations.maxFree', { max }, `The free version holds ${max} balance(s). A licence lifts that; nothing is removed in the meantime.`); } db.run(`INSERT INTO Valuation (assetId, valuedOn, accountId, balance, fiatRate, rateAt, source) VALUES (?,?,?,?,?,?,?)`, [v.assetId, v.valuedOn, v.accountId ?? null, v.balance, v.fiatRate, v.rateAt, v.source]); const id = db.lastId(); await db.persist(); emitChange('ledger-changed'); return id; }