125 lines
4.6 KiB
JavaScript
125 lines
4.6 KiB
JavaScript
import { config } from '../../config.js'
|
|
import { createDocument, deleteDocument, getDocument, listDocuments, Query, updateDocument } from '../appwriteAdmin.js'
|
|
|
|
/**
|
|
* Speicher fuer den Bankzugang (ein Dokument "finom" in bankConnections) und die Buchungen
|
|
* (bankTransactions). Die Dokument-ID einer Buchung ist der Hash ihrer Bank-Referenz (siehe bankSync),
|
|
* dieselbe Buchung landet also bei jedem Abruf im selben Dokument.
|
|
*/
|
|
|
|
/** ID des einen Verbindungsdokuments. */
|
|
export const CONNECTION_ID = 'finom'
|
|
|
|
// Felder, die von der Bank kommen und bei jedem Abruf ueberschrieben werden duerfen. Alles andere an einer
|
|
// Buchung (category, matched*, fixedCostId, note) gehoert dem Nutzer und bleibt beim Abruf unberuehrt.
|
|
const BANK_FIELDS = [
|
|
'status',
|
|
'bookingDate',
|
|
'valueDate',
|
|
'amountCents',
|
|
'currency',
|
|
'counterpartyName',
|
|
'counterpartyIban',
|
|
'remittance',
|
|
'mcc',
|
|
'entryReference',
|
|
'accountUid',
|
|
'syncedAt',
|
|
]
|
|
// Zuordnungsfelder einer neuen Buchung: leer (Spec: "category leer" = noch nicht zugeordnet)
|
|
const USER_FIELD_DEFAULTS = {
|
|
category: '',
|
|
matchedInvoiceId: '',
|
|
matchedInvoiceNumber: '',
|
|
matchedPayoutId: '',
|
|
fixedCostId: '',
|
|
note: '',
|
|
}
|
|
|
|
const withoutMeta = (doc) => Object.fromEntries(Object.entries(doc).filter(([key, value]) => !key.startsWith('$') && value !== undefined))
|
|
const pick = (doc, keys) => Object.fromEntries(keys.filter((key) => doc[key] !== undefined).map((key) => [key, doc[key]]))
|
|
const isStatus = (err, status) => err?.status === status
|
|
|
|
/**
|
|
* Dokument aktualisieren; gibt es das Dokument noch nicht (404), wird es mit createData angelegt. Meldet
|
|
* zwischen beiden Schritten ein anderer Aufruf dasselbe Dokument an (409), gilt wieder das Aktualisieren.
|
|
* Liefert 'neu' oder 'aktualisiert' (und das Dokument).
|
|
*/
|
|
async function upsert(collectionId, id, patch, createData) {
|
|
try {
|
|
return { how: 'aktualisiert', doc: await updateDocument(collectionId, id, patch) }
|
|
} catch (err) {
|
|
if (!isStatus(err, 404)) throw err
|
|
}
|
|
try {
|
|
return { how: 'neu', doc: await createDocument(collectionId, createData, id) }
|
|
} catch (err) {
|
|
if (!isStatus(err, 409)) throw err
|
|
}
|
|
return { how: 'aktualisiert', doc: await updateDocument(collectionId, id, patch) }
|
|
}
|
|
|
|
/** Der Bankzugang oder null, wenn es noch keinen gibt. */
|
|
export async function getConnection() {
|
|
try {
|
|
return await getDocument(config.collections.bankConnections, CONNECTION_ID)
|
|
} catch (err) {
|
|
if (isStatus(err, 404)) return null
|
|
throw err
|
|
}
|
|
}
|
|
|
|
/** Bankzugang anlegen oder die genannten Felder aktualisieren (die anderen bleiben). Liefert das Dokument. */
|
|
export async function saveConnection(patch) {
|
|
const data = withoutMeta(patch)
|
|
return (await upsert(config.collections.bankConnections, CONNECTION_ID, data, data)).doc
|
|
}
|
|
|
|
/**
|
|
* Buchung speichern. doc traegt die Dokument-ID in $id (so liefert sie toTransactionDoc). Neu: alle
|
|
* Bankfelder plus leere Zuordnungsfelder; vorhanden: nur die Bankfelder. 'neu' | 'aktualisiert'.
|
|
*/
|
|
export async function upsertTransaction(doc) {
|
|
const id = doc.$id
|
|
if (!id) throw new Error('upsertTransaction: $id fehlt')
|
|
const fields = pick(doc, BANK_FIELDS)
|
|
return (await upsert(config.collections.bankTransactions, id, fields, { ...USER_FIELD_DEFAULTS, ...fields })).how
|
|
}
|
|
|
|
/** Gespeicherte Buchungen, neueste zuerst nach Buchungsdatum (hoechstens limit). */
|
|
export async function listTransactions({ limit = 1000 } = {}) {
|
|
return listDocuments(config.collections.bankTransactions, [Query.orderDesc('bookingDate'), Query.limit(limit)])
|
|
}
|
|
|
|
/**
|
|
* Alle vorgemerkten Buchungen (Status PDNG). Die Suche nutzt den Index idx_status; fehlt er in Appwrite noch
|
|
* (Setup-Skript nicht erneut gelaufen) oder scheitert die Suche, wird in den gespeicherten Buchungen gefiltert,
|
|
* damit die Aufraeumung der Vormerkungen den Abruf nie bricht.
|
|
*/
|
|
export async function listPendingTransactions() {
|
|
try {
|
|
return await listDocuments(config.collections.bankTransactions, [Query.equal('status', 'PDNG'), Query.limit(1000)])
|
|
} catch {
|
|
return (await listTransactions()).filter((doc) => doc.status === 'PDNG')
|
|
}
|
|
}
|
|
|
|
/** Eine Buchung oder null. */
|
|
export async function getTransaction(id) {
|
|
try {
|
|
return await getDocument(config.collections.bankTransactions, id)
|
|
} catch (err) {
|
|
if (isStatus(err, 404)) return null
|
|
throw err
|
|
}
|
|
}
|
|
|
|
/** Felder einer Buchung aendern (z. B. Zuordnung, Notiz). */
|
|
export async function updateTransaction(id, patch) {
|
|
return updateDocument(config.collections.bankTransactions, id, withoutMeta(patch))
|
|
}
|
|
|
|
export async function deleteTransaction(id) {
|
|
return deleteDocument(config.collections.bankTransactions, id)
|
|
}
|