Buchungskalender: Aufbewahrung – Sperre wie Ablauf, Restpunkte

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 14:43:44 +00:00
1 parent c019d3c3b0
commit 827ee54875
5 files changed
+102 -17

No files matched your search

+68
View File
@@ -20,6 +20,7 @@ const { anonymisiere, loescheAbgeschaltete, loescheUkDaten, schliesseUkVonKunde
const jobs = await import('../server/services/unterkunft/jobs.js')
const mails = await import('../server/services/unterkunft/mails.js')
const { deleteCustomerWithPortalAccess } = await import('../server/services/customerAdmin.js')
const { withLocks } = await import('../server/services/keyedLock.js')
const { raeumeAuf } = await import('../server/services/termine/outbox.js')
const TAG = 24 * 3600 * 1000
@@ -149,6 +150,7 @@ test('Logs ohne Personendaten: ein Fehler wird gezaehlt, der Lauf macht weiter',
assert.equal(r.ipHashes, 1)
assert.deepEqual([holen(a).ipHash, holen(b).ipHash].sort(), ['', 'abcd'])
assert.ok(log.mock.calls.length >= 1)
assert.ok(!JSON.stringify(log.mock.calls.map((c) => c.arguments)).includes('eva@example.com'), 'keine Personendaten im Log')
})
const funktion = (over = {}) => aw.seed('portalFeatures', { $id: 'f1', customerId: 'c1', projectId: 'p1', featureKey: 'unterkunft', siteKey: 'abcdefghijklmnop', state: 'active', enabled: true, ...over })
@@ -236,3 +238,69 @@ test('Kundenloeschung schaltet die Funktion ab, schliesst die Unterkuenfte, Date
assert.equal(anzahl('f1'), 0)
assert.equal(await schliesseUkVonKunde('c1'), 0)
})
test('Sperre wie ablauf.js: ein Wechsel auf freigegeben unter der Unterkunfts-Sperre wird nicht ueberschrieben', async () => {
const id = anfrage({ $createdAt: vor(61), unterkunftId: 'u1' })
let lauf
await withLocks(['unterkunft:u1'], async () => {
// der Job laeuft an und wartet auf die Sperre; waehrenddessen gibt der Vermieter frei
lauf = anonymisiere(JETZT)
await new Promise((r) => setTimeout(r, 150))
await (await import('../server/services/appwriteAdmin.js')).updateDocument(config.collections.ukAnfragen, id, { status: 'freigegeben' })
})
const r = await lauf
assert.equal(r.abgelaufen, 0)
assert.equal(holen(id).status, 'freigegeben')
})
test('Gebucht ohne jede Rechnung: Rechnungsadresse geht mit den Gastdaten', async () => {
const a = anfrage({ status: 'gebucht', bis: '2022-06-01', rechnungsadresse: JSON.stringify(ADRESSE) })
const r = await anonymisiere(new Date('2026-01-02T10:00:00Z'))
assert.equal(r.gaeste, 1)
assert.deepEqual(gast(a), {})
assert.equal(holen(a).rechnungsadresse, null)
})
test('adminNote: bisheriger Text bleibt vor dem Marker; Wiedereinschalten bricht ab; erneutes Abschalten loescht nach neuen 90 Tagen; aktive Funktion mit altem Marker unberuehrt', async () => {
const sp = await import('../server/services/appwriteAdmin.js')
funktion({ state: 'disabled', enabled: false, disabledAt: vor(100), adminNote: 'Kunde gelöscht' })
datenFuer('f1')
assert.equal((await loescheAbgeschaltete(JETZT)).funktionen, 1)
assert.equal(aw.docs('portalFeatures').get('f1').adminNote, 'Kunde gelöscht – Buchungsdaten gelöscht am 2026-10-07')
// wieder eingeschaltet: nichts wird geloescht
funktion({ $id: 'f4', state: 'disabled', enabled: false, disabledAt: vor(91) })
datenFuer('f4')
await sp.updateDocument(config.collections.portalFeatures, 'f4', { state: 'active', enabled: true })
assert.equal((await loescheAbgeschaltete(JETZT)).funktionen, 0)
assert.equal(anzahl('f4'), 6)
// erneut abgeschaltet (alter Marker steht noch): erst nach neuen 90 Tagen
await sp.updateDocument(config.collections.portalFeatures, 'f4', { adminNote: 'Buchungsdaten gelöscht am 2026-01-01', state: 'disabled', enabled: false, disabledAt: vor(10) })
assert.equal((await loescheAbgeschaltete(JETZT)).funktionen, 0)
assert.equal(anzahl('f4'), 6)
assert.equal((await loescheAbgeschaltete(new Date(JETZT.getTime() + 81 * TAG))).funktionen, 1)
assert.equal(anzahl('f4'), 0)
// aktive Funktion mit altem Marker: unberuehrt
funktion({ $id: 'f5', state: 'active', adminNote: 'Buchungsdaten gelöscht am 2026-01-01' })
datenFuer('f5')
await loescheAbgeschaltete(new Date(JETZT.getTime() + 500 * TAG))
assert.equal(anzahl('f5'), 6)
})
test('Gemeinsamer Deckel: der Tagesjob gibt der Loeschung nur den Rest', async () => {
const modus = config.unterkunft.mode
const alt = config.unterkunft.aufbewahrungDeckel
try {
config.unterkunft.mode = 'on'
config.unterkunft.aufbewahrungDeckel = 3
for (let i = 0; i < 3; i++) anfrage({ $createdAt: vor(40) })
funktion({ state: 'disabled', enabled: false, disabledAt: vor(120) })
datenFuer('f1')
const r = await jobs.taeglicherJob(JETZT)
assert.equal(r.aufbewahrung.verbraucht, 3)
assert.equal(r.abgeschaltet.dokumente, 0)
assert.equal(r.abgeschaltet.gedeckelt, true)
} finally {
config.unterkunft.mode = modus
config.unterkunft.aufbewahrungDeckel = alt
}
})
+4 -2
View File
@@ -227,9 +227,11 @@ test('Jobs: im Modus off {aus:true}; Minutenjob ruft die Fristen ab, die anderen
config.unterkunft.mode = 'off'
assert.deepEqual(await jobs.minutenJob(T0), { aus: true })
assert.deepEqual(await jobs.viertelstundenJob(T0), { aus: true })
// Tagesjob: die Fristen gelten auch bei off, solange es Buchungsdaten gibt (dann laeuft er, sonst {aus:true})
// Tagesjob: die Fristen gelten auch bei off; ohne Buchungsdaten {aus:true}, mit Daten laeuft er
const tagAus = await jobs.taeglicherJob(T0)
assert.ok(tagAus.aus === true || tagAus.aufbewahrung?.fehler === 0, JSON.stringify(tagAus))
const hatDaten = aw.docs('ukEinstellungen').size > 0 || aw.docs('ukAnfragen').size > 0
if (hatDaten) assert.equal(tagAus.aufbewahrung?.fehler, 0, JSON.stringify(tagAus))
else assert.deepEqual(tagAus, { aus: true })
await leere()
config.unterkunft.mode = 'on'
const r = await jobs.minutenJob(T0)