// Generated, do not edit. sha256:d42fa49545ab26fee8488ce3ebd3d684d5394b406ae802e38e6b624c87d49f71 import { listSelectableAccounts } from '../data/accounts.js'; import { createEvent, findEventByRef, deleteEvent, listEvents, updateEvent } from '../data/events.js'; import { valuationGap, listOpenValuationDiffs, acceptValuationGap, listValuations } from '../data/valuations.js'; import { feeCandidates, withFee } from './valuation-fee.js'; import { t } from '../i18n/index.js'; import { assetLabelHtml } from '../ui/asset-label.js'; import * as dialog from '../ui/dialog.js'; import { esc } from '../ui/escape.js'; import { fmtUnit, val } from '../ui/format.js'; import { taskCardHtml, nothingPendingHtml } from '../ui/task-card.js'; import { lightningBalanceDiffs, recordLightningBalance } from '../data/positions.js'; import { onchainAsset } from '../data/tx-links.js'; import { spotOnDate } from '../ui/fiat.js'; import { priceToBase } from '../../shared/units.js'; import { dayNl, localDate, localDateNl } from '../../shared/time.js'; const meta = v => ({ decimals: v.asset_decimals, unit: v.asset_unit, subunit: v.asset_subunit, displayUnit: v.asset_display_unit }); const ADJUST_REF_PREFIX = 'val:'; export async function bookDifference(v, refresh) { if (!v || !v.accountId) return; const gap = await valuationGap(v); if (!gap) return; const refVan = (tegen) => (tegen ? `${ADJUST_REF_PREFIX}${v.id}:${tegen}` : `${ADJUST_REF_PREFIX}${v.id}`); const netVan = (e) => (e ? (e.toAccountId === v.accountId ? e.amount : -e.amount) : 0); const anderen = (await listSelectableAccounts({ ookId: null })).filter(a => a.id !== v.accountId); const kandidaten = [{ tegen: null, naam: null }, ...anderen.map(a => ({ tegen: a.id, naam: a.name }))]; const priors = new Map(); for (const k of kandidaten) { const e = await findEventByRef(refVan(k.tegen)); if (e) priors.set(k.tegen, e); } const bedragVan = (k) => gap + netVan(priors.get(k.tegen)); const keuzes = kandidaten.map(k => { const bedrag = bedragVan(k); const uit = bedrag < 0; const basis = k.tegen === null ? (uit ? t('valuations.bookAsSell') : t('valuations.bookAsBuy')) : `${uit ? t('valuations.bookAsTransferTo') : t('valuations.bookAsTransferFrom')} ${k.naam}`; const extra = !priors.has(k.tegen) ? '' : bedrag === 0 ? ` (${t('valuations.bookRemoves')})` : ` (${t('valuations.bookReplaces')} ${fmtUnit(Math.abs(bedrag), meta(v))})`; return { value: k.tegen === null ? 'trade' : `acc:${k.tegen}`, label: basis + extra }; }); const kandidatenFee = gap < 0 ? feeCandidates({ events: await listEvents(), valuations: await listValuations(), v }).slice(0, 10) : []; for (const e of kandidatenFee) { keuzes.push({ value: `fee:${e.id}`, label: t('valuations.bookAsFeeOn', { soort: t(`enums.event_type.${e.type}`), datum: localDateNl(e.occurredAt) }) }); } const res = await dialog.form({ title: t('valuations.bookTitle'), fields: [{ key: 'hoe', type: 'select', value: 'trade', options: keuzes, label: `${fmtUnit(Math.abs(gap), meta(v))} ${t('valuations.bookAsSuffix')}` }], confirmLabel: t('valuations.book'), }); if (!res) return; if (res.hoe.startsWith('fee:')) { const e = kandidatenFee.find(x => x.id === Number(res.hoe.slice(4))); if (e) await updateEvent(e.id, withFee(e, gap, t('valuations.bookFeeNote'))); refresh(); return; } const tegen = res.hoe.startsWith('acc:') ? Number(res.hoe.slice(4)) : null; const prior = priors.get(tegen) || null; const bedrag = gap + netVan(prior); const uit = bedrag < 0; if (prior) await deleteEvent(prior.id); if (!bedrag) { refresh(); return; } const gemeen = { assetId: v.assetId, occurredAt: v.rateAt, amount: Math.abs(bedrag), fiatFee: null, unitFee: null, source: 'reconcile', note: t('valuations.bookNote'), importReference: refVan(tegen), }; await createEvent(tegen ? { ...gemeen, type: 'transfer', fiatPrice: null, fromAccountId: uit ? v.accountId : tegen, toAccountId: uit ? tegen : v.accountId } : { ...gemeen, type: uit ? 'sell' : 'buy', fiatPrice: v.fiatRate, fromAccountId: uit ? v.accountId : null, toAccountId: uit ? null : v.accountId }); refresh(); } export async function acceptDifference(id, refresh) { if (!(await dialog.confirm({ message: t('valuations.confirmAccept') }))) return; await acceptValuationGap(Number(id)); refresh(); } async function recordMeasured(d, refresh) { const asset = await onchainAsset(); const dag = localDate(d.scannedAt); let koers = null; try { koers = asset ? await spotOnDate(asset, dag) : null; } catch { koers = null; } const res = await dialog.form({ title: t('valuations.recordTitle'), fields: [{ key: 'rate', type: 'number', value: koers != null ? String(Math.round(koers * 100) / 100) : '', label: t('valuations.recordRate', { date: dayNl(dag) }), attrs: { min: 0, step: 'any' } }], confirmLabel: t('valuations.recordBalance'), }); if (!res) return; const rate = parseFloat(String(res.rate).replace(',', '.')); if (!(rate > 0)) return; try { await recordLightningBalance(d.accountId, { valuedOn: dag, fiatRate: priceToBase(rate, asset) }); } catch (err) { await dialog.alert({ message: err.message }); return; } refresh(); } export async function renderValuationWidget(host, refresh) { const [rows, gemeten] = await Promise.all([listOpenValuationDiffs(), lightningBalanceDiffs()]); const btc = gemeten.length ? await onchainAsset() : null; const btcMeta = btc ? { decimals: btc.decimals, unit: btc.unit, subunit: btc.subunit, displayUnit: btc.displayUnit } : null; const nodeRijen = gemeten.map((d, i) => `