diff --git a/scripts/setup-finanzen.mjs b/scripts/setup-finanzen.mjs index b52e027..8a6bf25 100644 --- a/scripts/setup-finanzen.mjs +++ b/scripts/setup-finanzen.mjs @@ -187,7 +187,11 @@ await ensureCollection( { type: 'string', key: 'note', size: 500 }, { type: 'string', key: 'syncedAt', size: 32 }, ], - [{ key: 'idx_booking', type: 'key', attributes: ['bookingDate'] }] + [ + { key: 'idx_booking', type: 'key', attributes: ['bookingDate'] }, + // Abruf: vorgemerkte Buchungen (PDNG) suchen + { key: 'idx_status', type: 'key', attributes: ['status'] }, + ] ) // Einstellungen: ein Dokument "main" diff --git a/server/services/finance/bankStore.js b/server/services/finance/bankStore.js index 407bc90..883666c 100644 --- a/server/services/finance/bankStore.js +++ b/server/services/finance/bankStore.js @@ -91,9 +91,17 @@ export async function listTransactions({ limit = 1000 } = {}) { return listDocuments(config.collections.bankTransactions, [Query.orderDesc('bookingDate'), Query.limit(limit)]) } -/** Alle vorgemerkten Buchungen (Status PDNG). */ +/** + * 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() { - return listDocuments(config.collections.bankTransactions, [Query.equal('status', 'PDNG'), Query.limit(1000)]) + 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. */ diff --git a/test/financeBankSync.test.js b/test/financeBankSync.test.js index 1f22bd5..c3d64e8 100644 --- a/test/financeBankSync.test.js +++ b/test/financeBankSync.test.js @@ -385,6 +385,23 @@ test('syncBank: gleiche entry_reference bei Vormerkung und Buchung -> ein Dokume assert.equal(txDocs().get(docId('same')).status, 'BOOK') }) +test('syncBank: scheitert die Suche nach Vormerkungen, wird im Speicher gefiltert und der Abruf endet trotzdem ok', async () => { + await reset({ transactions: [...nine(), raw({ ref: 'p1', date: '2026-09-30', amount: '12.00', name: 'VORGEMERKT', status: 'PDNG' })] }) + await connect() + assert.equal((await syncBank({ now: NOW })).neu, 10) + assert.equal(txDocs().get(docId('p1')).status, 'PDNG') + + // die Bank nennt die Vormerkung nicht mehr; die Suche nach status=PDNG (erste Lese-Anfrage an bankTransactions) scheitert + fake.transactions.set(ACCOUNT, nine()) + aw.failNext({ collection: config.collections.bankTransactions, method: 'GET', status: 400, message: 'Nachbau: Index status fehlt' }) + const result = await syncBank({ now: NOW }) + assert.deepEqual(result, { ok: true, neu: 0, aktualisiert: 9, geloescht: 1 }) + assert.equal(txDocs().has(docId('p1')), false) + assert.equal(txDocs().size, 9) + assert.equal(connection().lastSyncOk, true) + assert.equal(connection().lastError, '') +}) + test('syncBank: Vormerkungen vor dem Abrufzeitraum bleiben, gebuchte Dokumente werden nie gelöscht', async () => { await reset() await connect() diff --git a/test/financeSetup.test.js b/test/financeSetup.test.js index 4828102..f46b55e 100644 --- a/test/financeSetup.test.js +++ b/test/financeSetup.test.js @@ -1,5 +1,6 @@ import { test } from 'node:test' import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' import { schemaKeys } from './support/schema.js' test('bankTransactions: Attribute der Spec sind im Setup-Skript angelegt', () => { @@ -28,3 +29,11 @@ test('subscriptions wird weiter aus dem Abo-Setup gelesen', () => { test('unbekannte Collection scheitert mit klarer Meldung', () => { assert.throws(() => schemaKeys('gibtEsNicht'), /Collection gibtEsNicht fehlt im Setup-Skript/) }) + +test('bankTransactions: Indexe auf bookingDate (Sortierung) und status (Vormerkungen suchen)', () => { + const src = readFileSync(new URL('../scripts/setup-finanzen.mjs', import.meta.url), 'utf8') + const start = src.indexOf("'bankTransactions',") + const block = src.slice(start, src.indexOf("// Einstellungen", start)) + assert.match(block, /key: 'idx_booking', type: 'key', attributes: \['bookingDate'\]/) + assert.match(block, /key: 'idx_status', type: 'key', attributes: \['status'\]/) +})