Buchungskalender: Abschluss – S5 Hinweis-Mail K35 vor der Loeschung, Rechnungen im Zustand abgeschaltet lesbar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-10-07 15:23:56 +00:00
1 parent e2fc4eb676
commit ab838d07bc
6 files changed
+225 -4

No files matched your search

+68 -1
View File
@@ -15,13 +15,16 @@ after(() => aw.close())
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1/v1' })
const { config } = await import('../server/config.js')
const { deleteDocument } = await import('../server/services/appwriteAdmin.js')
const { deleteDocument, updateDocument } = await import('../server/services/appwriteAdmin.js')
const { anonymisiere, loescheAbgeschaltete, loescheUkDaten, schliesseUkVonKunde } = await import('../server/services/unterkunft/aufbewahrung.js')
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 { sendeHinweiseVorLoeschung } = await import('../server/services/unterkunft/abschaltHinweis.js')
const { setMailTransportForTests } = await import('../server/services/mailer.js')
const { mailSink } = await import('./support/mailSink.js')
const TAG = 24 * 3600 * 1000
const JETZT = new Date('2026-10-07T10:00:00.000Z')
@@ -183,6 +186,70 @@ test('Abgeschaltete Funktion: nach 89 Tagen alles da, nach 91 Tagen alle uk*-Dat
assert.equal(anzahl('f3'), 6)
})
test('K35: einmal pro Abschaltung, mit Loeschdatum, nur mit Daten; Marker; ohne Adresse nur Marker; Fehler wird wiederholt; Tagesjob sendet vor der Loeschung', async () => {
const senke = mailSink()
setMailTransportForTests(senke)
try {
aw.seed('customers', { $id: 'c1', name: 'Mimi', email: 'mimi@example.com' })
aw.seed('websiteProjects', { $id: 'p1', customerId: 'c1', projectName: 'Donau Home' })
funktion({ state: 'disabled', enabled: false, disabledAt: vor(1) })
funktion({ $id: 'f2', state: 'disabled', enabled: false, disabledAt: vor(1), customerId: 'c9' }) // Kunde fehlt
funktion({ $id: 'f3', state: 'disabled', enabled: false, disabledAt: vor(1) }) // ohne Daten
funktion({ $id: 'f4', state: 'active' })
for (const f of ['f1', 'f2', 'f4']) datenFuer(f)
const r = await sendeHinweiseVorLoeschung(JETZT)
assert.deepEqual([r.gesendet, r.ohneAdresse, r.fehler], [1, 1, 0])
assert.equal(senke.sent.length, 1)
const m = senke.sent[0]
assert.equal(m.to[0].address, 'mimi@example.com')
assert.match(m.subject, /Buchungskalender abgeschaltet/)
assert.match(m.text, /Donau Home/)
assert.match(m.text, /04.01.2027/, 'disabledAt + 90 Tage')
assert.match(aw.docs('portalFeatures').get('f1').adminNote, /K35 am 2026-10-07/)
assert.match(aw.docs('portalFeatures').get('f2').adminNote, /K35 am 2026-10-07/)
assert.ok(!aw.docs('portalFeatures').get('f3').adminNote)
assert.ok(!aw.docs('portalFeatures').get('f4').adminNote)
// zweiter Lauf: nichts mehr
assert.equal((await sendeHinweiseVorLoeschung(new Date(JETZT.getTime() + TAG))).gesendet, 0)
assert.equal(senke.sent.length, 1)
// erneut abgeschaltet (alter Marker liegt vor disabledAt): wieder ein Hinweis
await updateDocument(config.collections.portalFeatures, 'f1', { disabledAt: vor(-5) })
assert.equal((await sendeHinweiseVorLoeschung(new Date(JETZT.getTime() + 6 * TAG))).gesendet, 1)
// Versandfehler: kein Marker, naechster Lauf wiederholt
aw.seed('portalFeatures', { ...aw.docs('portalFeatures').get('f3'), adminNote: '' })
datenFuer('f3')
setMailTransportForTests({ async sendMail() { throw new Error('smtp down') } })
const log = console.warn
console.warn = () => {}
const f = await sendeHinweiseVorLoeschung(JETZT)
console.warn = log
assert.equal(f.fehler, 1)
assert.ok(!aw.docs('portalFeatures').get('f3').adminNote)
setMailTransportForTests(senke)
assert.equal((await sendeHinweiseVorLoeschung(JETZT)).gesendet, 1)
} finally {
setMailTransportForTests(null)
}
})
test('K35: Tagesjob schickt den Hinweis und loescht erst nach 90 Tagen', async () => {
const senke = mailSink()
setMailTransportForTests(senke)
try {
aw.seed('customers', { $id: 'c1', name: 'Mimi', email: 'mimi@example.com' })
funktion({ state: 'disabled', enabled: false, disabledAt: vor(3) })
datenFuer('f1')
await jobs.taeglicherJob(JETZT)
assert.equal(senke.sent.length, 1)
assert.equal(anzahl('f1'), 6)
await jobs.taeglicherJob(new Date(JETZT.getTime() + 88 * TAG))
assert.equal(senke.sent.length, 1)
assert.equal(anzahl('f1'), 0)
} finally {
setMailTransportForTests(null)
}
})
test('loescheUkDaten mit Deckel: unvollstaendig, Fortsetzung loescht den Rest', async () => {
datenFuer('f1')
const a = await loescheUkDaten({ featureId: 'f1', deckel: 2 })