Files
2026-10-29 11:57:29 +01:00

75 lines
3.2 KiB
JavaScript

// Generated, do not edit. sha256:f8255fd8148a61f4a49cc0a84d161f8b1bc531a1a315349c9aef9328871b9d6f
import * as db from '../db.js';
import { fail } from '../../shared/app-error.js';
import { assertTimestamp, nowIso } from './_shared.js';
import { createEvent, deleteEvent, EVENT_JOIN } from './events.js';
import { trackUpdate } from './change-log.js';
import { emitChange } from './_events.js';
export function listReservations() {
return db.all(`${EVENT_JOIN} WHERE e.type = 'reservation' ORDER BY e.occurredAt ASC, e.id ASC`);
}
export function listReservationsForAccount(accountId) {
return db.all(`${EVENT_JOIN} WHERE e.type = 'reservation' AND (e.fromAccountId = ?1 OR e.toAccountId = ?1)
ORDER BY e.occurredAt ASC, e.id ASC`, [accountId]);
}
export function listDueReservations(untilIso = nowIso()) {
return db.all(`${EVENT_JOIN} WHERE e.type = 'reservation' AND e.occurredAt <= ?
ORDER BY e.occurredAt ASC, e.id ASC`, [untilIso]);
}
function rowOf(r) {
if (!r.accountId) throw new Error('A reservation needs an account.');
if (r.direction !== 'in' && r.direction !== 'out') throw new Error(`Unknown direction '${r.direction}'; use 'in' or 'out'.`);
return {
assetId: r.assetId, type: 'reservation', occurredAt: r.occurredAt, amount: r.amount,
fromAccountId: r.direction === 'out' ? r.accountId : null,
toAccountId: r.direction === 'in' ? r.accountId : null,
source: 'manual', note: r.note ?? null,
};
}
function openReservation(id) {
const row = db.one(`SELECT * FROM Event WHERE id = ?`, [id]);
if (!row || row.type !== 'reservation') fail('reservations.notOpen', null, 'This reservation is no longer open.');
return row;
}
export async function createReservation(r) {
return createEvent(rowOf(r));
}
export async function updateReservation(id, r) {
openReservation(id);
const e = rowOf(r);
trackUpdate('Event', id, () => db.run(
`UPDATE Event SET assetId = ?, occurredAt = ?, amount = ?, fromAccountId = ?, toAccountId = ?, note = ?
WHERE id = ? AND type = 'reservation'`,
[e.assetId, e.occurredAt, e.amount, e.fromAccountId, e.toAccountId, e.note, id]));
await db.persist();
emitChange('ledger-changed');
}
export async function deleteReservation(id) {
openReservation(id);
await deleteEvent(id);
}
export async function bookReservation(id, { occurredAt, amount, otherAccountId = null, note }) {
const row = openReservation(id);
assertTimestamp('occurredAt', occurredAt);
if (!Number.isInteger(amount) || amount <= 0) throw new Error(`A booking needs an amount > 0 in the smallest unit (got ${amount}).`);
const out = row.fromAccountId != null;
const own = out ? row.fromAccountId : row.toAccountId;
if (otherAccountId != null && otherAccountId === own) throw new Error('The other side of a booking is another account.');
const [from, to] = out ? [own, otherAccountId] : [otherAccountId, own];
trackUpdate('Event', id, () => db.run(
`UPDATE Event SET type = 'transfer', occurredAt = ?, amount = ?, fromAccountId = ?, toAccountId = ?, note = ?
WHERE id = ? AND type = 'reservation'`,
[occurredAt, amount, from, to, note === undefined ? row.note : (note || null), id]));
await db.persist();
emitChange('ledger-changed');
}