Öffnungszeiten: Daten bei Kundenlöschung entfernen
Kunde löschen schaltet die Funktion zeiten wie den Terminplaner ab (Grund kunde_geloescht) und löscht Zeiten, Erinnerungsmerker und Antwortspeicher sofort; Fehler halten die Kundenlöschung nicht auf. Die Löschfristen löschen die Zeiten vor dem Abschalten und zählen sie im Bericht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
efc6dcf332
commit
9a14e874c0
3 files changed
+282
-3
No files matched your search
@@ -291,6 +291,22 @@ export async function deleteCustomerWithPortalAccess(customerId) {
|
||||
console.error(`[customerAdmin] Terminplaner von Kunde ${customerId} nicht abgeschaltet:`, err.message)
|
||||
}
|
||||
|
||||
// Oeffnungszeiten live: wie der Terminplaner abschalten (die Antwort der Website ist sofort aktiv false: Kunde und Funktion fehlen/sind
|
||||
// disabled), danach Zeiten und Erinnerungsmerker sofort loeschen (Spec 5) und den Speicher der Antworten leeren. Beides fuer sich in
|
||||
// try/catch: ein Fehler haelt weder das Loeschen des Kunden noch den jeweils anderen Schritt auf. Geloggt wird nur die Kunden-ID.
|
||||
try {
|
||||
const { disableCustomerFeatures } = await import('./siteFeatures.js')
|
||||
await disableCustomerFeatures(customerId, 'zeiten', { adminNote: 'Kunde gelöscht', reason: 'kunde_geloescht' })
|
||||
} catch (err) {
|
||||
console.error(`[customerAdmin] Öffnungszeiten von Kunde ${customerId} nicht abgeschaltet:`, err.message)
|
||||
}
|
||||
try {
|
||||
const { loescheZeitenVonKunde } = await import('./zeiten/speicher.js')
|
||||
await loescheZeitenVonKunde(customerId)
|
||||
} catch (err) {
|
||||
console.error(`[customerAdmin] Öffnungszeiten von Kunde ${customerId} nicht gelöscht:`, err.message)
|
||||
}
|
||||
|
||||
// Entwuerfe (Terminplaner im Zustand off: nie bestellt oder nach dem Abo-Ende zurueckgesetzt) gehen sofort mit dem Kunden, Funktionsdokument
|
||||
// und Einstellung. Fehler halten das Loeschen nicht auf (wie oben); ein uebrig gebliebener Entwurf faellt nach 90 Tagen im Tageslauf.
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,10 @@ import { filesOf, STATUS } from './takeoverCredits.js'
|
||||
* - News-Pinnwand: Wird ein Kunde geloescht (Frist wie oben), loeschen wir auch seine News-Beitraege samt Fotos
|
||||
* (newsPosts, Bilddateien) und setzen seine Funktionen (portalFeatures) auf disabled. Der Plan fuehrt sie in
|
||||
* plan.news (nur Kunden mit Beitraegen oder noch nicht abgeschalteten Funktionen), damit plan.customers unveraendert bleibt.
|
||||
* - Oeffnungszeiten live: Zeigt der Plan Funktionen 'zeiten' (plan.news[].zeitenIds, Teilmenge von featureIds), loescht der Lauf vor dem
|
||||
* Abschalten die Zeiten und Erinnerungsmerker des Kunden (loescheZeitenVonKunde, per dynamischem Import wie bei siteFeatures).
|
||||
* Scheitert das, werden diese Funktionen nicht abgeschaltet (solange Daten da sind, soll nichts "aus" aussehen); der Fehler steht unter
|
||||
* "Fehlgeschlagen" im Bericht.
|
||||
* Zeitrechnung in deutscher Zeit: Silvester 23:30 in Berlin ist noch das alte Jahr.
|
||||
* Nicht abgedeckt: Vertragsereignisse ohne customerId bzw. ohne zugehoeriges Abo-Dokument (Waisen) werden
|
||||
* weder gefunden noch geloescht; sie muessen von Hand geprueft werden. Listen werden nicht paginiert: wird eine
|
||||
@@ -117,6 +121,7 @@ export async function findDueDeletions(now = new Date(), { limit = LIST_LIMIT }
|
||||
customerId,
|
||||
posts: posts.map((p) => ({ id: p.$id, projectId: p.projectId, imageId: p.imageId || null })),
|
||||
featureIds: features.map((f) => f.$id),
|
||||
zeitenIds: features.filter((f) => f.featureKey === 'zeiten').map((f) => f.$id),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -165,12 +170,12 @@ async function deleteQuietly(collection, id) {
|
||||
* Appwrite-Dokumente. Lehnt Stripe ab (beendete Abos lassen sich oft nicht mehr aendern), steht das Abo in manual;
|
||||
* die Appwrite-Dokumente werden trotzdem geloescht, denn die Frist ist abgelaufen.
|
||||
* @returns {Promise<{ deleted: { subscriptions: number, contractEvents: number, takeover: number, stripeCleared: number,
|
||||
* newsPosts: number, newsImages: number, features: number },
|
||||
* newsPosts: number, newsImages: number, features: number, zeiten: number },
|
||||
* manual: { customerId: string, subscriptionId: string, reason: string }[],
|
||||
* failed: { what: string, id: string, reason: string }[] }>}
|
||||
*/
|
||||
export async function applyDeletions(plan) {
|
||||
const deleted = { subscriptions: 0, contractEvents: 0, takeover: 0, stripeCleared: 0, newsPosts: 0, newsImages: 0, features: 0 }
|
||||
const deleted = { subscriptions: 0, contractEvents: 0, takeover: 0, stripeCleared: 0, newsPosts: 0, newsImages: 0, features: 0, zeiten: 0 }
|
||||
const manual = []
|
||||
const failed = []
|
||||
const clear = Object.fromEntries(CONSENT_METADATA_KEYS.map((key) => [key, '']))
|
||||
@@ -217,7 +222,19 @@ export async function applyDeletions(plan) {
|
||||
}
|
||||
if (await attempt('newsPosts', post.id, () => deleteQuietly(NEWS_POSTS(), post.id))) deleted.newsPosts += 1
|
||||
}
|
||||
// Oeffnungszeiten: erst die Daten (Dokumente, Merker, Speicher), dann das Abschalten; scheitert das Loeschen, bleiben diese Funktionen
|
||||
// unangetastet und der Fehler steht im Bericht (wie beim Bild vor dem News-Beitrag)
|
||||
const zeitenIds = new Set(entry.zeitenIds || [])
|
||||
let zeitenWeg = true
|
||||
if (zeitenIds.size) {
|
||||
zeitenWeg = await attempt('oeffnungszeiten', entry.customerId, async () => {
|
||||
const { loescheZeitenVonKunde } = await import('./zeiten/speicher.js')
|
||||
deleted.zeiten += await loescheZeitenVonKunde(entry.customerId)
|
||||
return true
|
||||
})
|
||||
}
|
||||
for (const id of entry.featureIds) {
|
||||
if (!zeitenWeg && zeitenIds.has(id)) continue
|
||||
const off = await attempt('portalFeatures', id, async () => {
|
||||
// disable() meldet bei Analyse auch den Sammeldienst ab (best effort)
|
||||
await disable(id, { adminNote: 'Abgeschaltet: Aufbewahrungsfrist abgelaufen' })
|
||||
@@ -251,7 +268,8 @@ export function formatReport(plan, result = null) {
|
||||
}
|
||||
for (const n of plan.news || []) {
|
||||
const images = n.posts.filter((p) => p.imageId).length
|
||||
lines.push(`Kunde ${n.customerId}: ${n.posts.length} News-Beitrag/-Beiträge mit ${images} Foto(s) zu löschen, ${n.featureIds.length} Funktion(en) abzuschalten`)
|
||||
const zeiten = n.zeitenIds?.length ? `; Öffnungszeiten-Daten von ${n.zeitenIds.length} Funktion(en) zu löschen` : ''
|
||||
lines.push(`Kunde ${n.customerId}: ${n.posts.length} News-Beitrag/-Beiträge mit ${images} Foto(s) zu löschen, ${n.featureIds.length} Funktion(en) abzuschalten${zeiten}`)
|
||||
}
|
||||
const del = plan.takeover.filter((t) => t.action === 'loeschen')
|
||||
const report = plan.takeover.filter((t) => t.action === 'melden')
|
||||
@@ -270,6 +288,7 @@ export function formatReport(plan, result = null) {
|
||||
`Gelöscht: ${d.subscriptions} Abo(s), ${d.contractEvents} Vertragsereignis(se), ${d.takeover} Übernahme-Antrag/-Anträge; Stripe-Metadaten geleert bei ${d.stripeCleared} Abo(s).`,
|
||||
`News: ${d.newsPosts} Beitrag/Beiträge und ${d.newsImages} Foto(s) gelöscht, ${d.features} Funktion(en) abgeschaltet.`
|
||||
)
|
||||
if (d.zeiten) lines.push(`Öffnungszeiten: ${d.zeiten} Datensatz/Datensätze gelöscht.`)
|
||||
if (result.failed?.length) {
|
||||
lines.push('', 'Fehlgeschlagen (Lauf erneut starten):', ...result.failed.map((f) => ` ${f.what} ${f.id}: ${f.reason}`))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user