Kundenansicht P6: Widerruf des Wechsels beendet Wartungs-Funktionen, die Analyse bleibt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d5a7312896
commit
df53cd039b
3 files changed
+104
-5
No files matched your search
+17
-4
@@ -24,7 +24,7 @@ import {
|
||||
import { assertOwnProject, handleFeatureCheckoutEvent, isFeatureCheckoutEvent, listCustomerFeatureDocs, paidFeatureOrders } from '../services/siteFeatures.js'
|
||||
import { featureConfig } from '../services/featureRegistry.js'
|
||||
import { istAboModell } from '../services/aboModell.js'
|
||||
import { ENTHALTEN_IN } from '../services/aboFunktionen.js'
|
||||
import { ENTHALTEN_IN, aboModellFuerKunde } from '../services/aboFunktionen.js'
|
||||
import { neueAnsicht } from '../services/kundenansicht/schalter.js'
|
||||
import { PRODUCT_KEYS, contractAttachmentFor, describeItems, isTermsV4OrNewer, productLabel, productView, termsDocFor } from '../services/products.js'
|
||||
import {
|
||||
@@ -1139,10 +1139,15 @@ async function withdrawOrder(sub, d, receivedAt) {
|
||||
}
|
||||
}
|
||||
if (inTime && type === 'wechsel') {
|
||||
// Ohne Wartungs-Abo gibt es keine Website-Analyse: Messdienst abschalten (der Widerruf scheitert nie daran)
|
||||
// Ohne Wartungs-Abo gibt es keine Website-Analyse: Messdienst abschalten (der Widerruf scheitert nie daran). Nicht in der neuen Ansicht
|
||||
// (Kundenansicht neu, P6): dort enthaelt auch das Hosting-Abo die Analyse, sie bleibt. Ist das nicht lesbar, bleibt sie ebenfalls (der
|
||||
// taegliche Abo-Check der Analyse raeumt bei Bedarf auf).
|
||||
try {
|
||||
const { endAnalyseForCustomer } = await import('../services/analyse.js')
|
||||
await endAnalyseForCustomer(sub.customerId, { reason: 'widerruf_wechsel', mail: true, now: receivedAt })
|
||||
const neueAnsichtKunde = await aboModellFuerKunde(sub.customerId)
|
||||
if (!neueAnsichtKunde) {
|
||||
const { endAnalyseForCustomer } = await import('../services/analyse.js')
|
||||
await endAnalyseForCustomer(sub.customerId, { reason: 'widerruf_wechsel', mail: true, now: receivedAt })
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[abo] Analyse nach Widerruf des Wechsels (Abo ${sub.$id}) nicht beendet:`, err.message)
|
||||
}
|
||||
@@ -1155,6 +1160,14 @@ async function withdrawOrder(sub, d, receivedAt) {
|
||||
} catch (err) {
|
||||
console.error(`[abo] Terminplaner nach Widerruf des Wechsels (Abo ${sub.$id}) nicht beendet:`, err.message)
|
||||
}
|
||||
// Funktionen des Abo-Modells (News, Terminplaner, Webshop, Buchungskalender, Oeffnungszeiten live; auch im Projekt eingebaute): ebenso sofort,
|
||||
// K39 mit dem Hosting-Satz; was misslingt, holt der Tagesabgleich nach
|
||||
try {
|
||||
const { beendeWartungsFunktionen } = await import('../services/aboFunktionenAbgleich.js')
|
||||
await beendeWartungsFunktionen(sub.customerId, { now: receivedAt })
|
||||
} catch (err) {
|
||||
console.error(`[abo] Funktionen des Abo-Modells nach Widerruf des Wechsels (Abo ${sub.$id}) nicht beendet:`, err.message)
|
||||
}
|
||||
}
|
||||
const event = await recordDeclarationEvent(sub, 'widerruf', receivedAt, { order: d.order, eventId: ids[0] ?? null, eventIds: ids, inTime }, 'offen')
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Query, deleteDocument, getDocument, listAllDocuments, updateDocument }
|
||||
import { withLocks } from './keyedLock.js'
|
||||
import { deleteNewsImage } from './newsImages.js'
|
||||
import { listPosts } from './newsPosts.js'
|
||||
import { activate, disable, stateFields } from './siteFeatures.js'
|
||||
import { activate, disable, listCustomerFeatureDocs, stateFields } from './siteFeatures.js'
|
||||
import { pruefeNow } from './termine/util.js'
|
||||
import { loeschTag } from './termine/frist.js'
|
||||
import { ladeEinstellung } from './termine/kontext.js'
|
||||
@@ -40,6 +40,11 @@ import { leereCache } from './unterkunft/kontext.js'
|
||||
* (wie bei News, der Vermerk endet damit), unabhaengig vom Abo. Ohne Vermerk bleibt alles. Der Terminplaner wird von
|
||||
* termine/jobs.js selbst auf off gesetzt.
|
||||
*
|
||||
* Sofortpfad (V11, N27): beendeWartungsFunktionen(customerId) beendet alle Abo-Modell-Funktionen eines Kunden, die nur im Wartungs-Abo stecken,
|
||||
* sofort, wenn dessen Wechsel ins Wartungs-Abo in der Frist widerrufen wird (routes/abo.js, withdrawOrder). Ohne Abo-Pruefung (der Spiegel
|
||||
* steht bis zur Handarbeit des Teams noch auf Wartung); die Analyse gehoert zu Hosting und bleibt. Die Mail K39 geht wie im Tagesabgleich
|
||||
* (meldeBeendet), ausser fuer zeiten: dort sendet der Zeiten-Abgleich K41 samt Ersatz-Ticket.
|
||||
*
|
||||
* Regeln wie termine/aboAbgleich.js: eine Listenabfrage fuer alle Kunden; das Abo je Kunde einmal fuer die Vorauswahl lesen. Geschrieben
|
||||
* wird nur unter der Sperre feature-admin:<projectId> nach frischem Lesen von Dokument UND Abo (nie aus der Vorauswahl); die Mail und der
|
||||
* Marker folgen nach der Sperre. Fail-closed: ist etwas nicht lesbar (Liste, Abo, Dokument, Aufbauphase), wird nichts abgeschaltet; Kunde
|
||||
@@ -52,6 +57,7 @@ export const ABGLEICH_KEYS = Object.freeze(['news', 'termine', 'shop', 'unterkun
|
||||
const GRUND = 'abo_ende'
|
||||
const NOTIZ = 'System: Abgeschaltet: Abo beendet'
|
||||
const NOTIZ_WIEDER = 'System: Wieder eingeschaltet: Abo läuft wieder'
|
||||
const NOTIZ_WECHSEL = 'Abgeschaltet: Wechsel widerrufen'
|
||||
|
||||
const ADRESSE = /[^\s<>"',;()]+@[^\s<>"',;()]+/g
|
||||
|
||||
@@ -300,6 +306,50 @@ export async function gleicheAboFunktionenAb(now = new Date()) {
|
||||
return erg
|
||||
}
|
||||
|
||||
/** Steckt die Funktion nur im Wartungs-Abo (ENTHALTEN_IN genau ['wartung'])? Die Analyse (auch Hosting) nicht. */
|
||||
const nurImWartungsAbo = (key) => Object.hasOwn(ENTHALTEN_IN, key) && ENTHALTEN_IN[key].length === 1 && ENTHALTEN_IN[key][0] === 'wartung'
|
||||
|
||||
/**
|
||||
* Sofortpfad beim Widerruf des Wechsels ins Wartungs-Abo (V11, N27): alle Dokumente des Abo-Modells (auch projekt) des Kunden im Zustand
|
||||
* setup oder active, deren Funktion nur im Wartungs-Abo steckt (news, termine, shop, unterkunft, zeiten; nicht analyse), werden sofort
|
||||
* abgeschaltet (Grund abo_ende, Notiz "Abgeschaltet: Wechsel widerrufen"), je Dokument unter feature-admin:<projectId> nach frischem Lesen
|
||||
* und ohne Abo-Pruefung. Danach, ausserhalb der Sperre, K39 mit dem Hosting-Satz (meldeBeendet, art hosting) fuer alle ausser zeiten (dort
|
||||
* sendet der Zeiten-Abgleich K41 samt Ersatz-Ticket). Ein Fehler bei einem Dokument stoppt die uebrigen nicht; was misslingt, holt der
|
||||
* Tagesabgleich nach (aboProdukt kennt den widerrufenen Wechsel). Wirft nie; das Log nennt weder Namen noch Adressen.
|
||||
* @param {string} customerId
|
||||
* @param {{ now?: Date }} [options] Zeitpunkt des Widerrufs (Marker aboEndeMailAt)
|
||||
* @returns {Promise<{ beendet: string[] }>} die IDs der abgeschalteten Funktionsdokumente
|
||||
*/
|
||||
export async function beendeWartungsFunktionen(customerId, { now = new Date() } = {}) {
|
||||
const beendet = []
|
||||
if (!customerId) return { beendet }
|
||||
try {
|
||||
const kandidaten = (await listCustomerFeatureDocs(customerId)).filter(
|
||||
(doc) => istAboModell(doc) && doc.projectId && (doc.state === 'setup' || doc.state === 'active') && nurImWartungsAbo(doc.featureKey)
|
||||
)
|
||||
for (const doc of kandidaten) {
|
||||
try {
|
||||
const abgeschaltet = await withLocks([`feature-admin:${doc.projectId}`], async () => {
|
||||
const frisch = await getDocument(portalFeatures(), doc.$id)
|
||||
if (!istAboModell(frisch) || (frisch.state !== 'setup' && frisch.state !== 'active')) return null // das Team war schneller
|
||||
const aus = await disable(frisch.$id, { reason: GRUND, adminNote: NOTIZ_WECHSEL })
|
||||
if (aus.state !== 'disabled' || aus.disabledReason !== GRUND) return null // jemand war zwischen Lesen und Schreiben schneller
|
||||
if (aus.featureKey === 'unterkunft' && aus.siteKey) leereCache(aus.siteKey)
|
||||
return aus
|
||||
})
|
||||
if (!abgeschaltet) continue
|
||||
beendet.push(abgeschaltet.$id)
|
||||
if (abgeschaltet.featureKey !== 'zeiten') await meldeBeendet(abgeschaltet, { art: 'hosting', now }) // nach der Sperre; bei Misserfolg wiederholt der Tagesabgleich
|
||||
} catch (err) {
|
||||
console.error(`[abo-funktionen] Sofortpfad: Funktion ${doc.$id} nicht vollständig beendet, der Tagesabgleich holt nach: ${fehlerText(err)}`)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[abo-funktionen] Sofortpfad: Funktionen eines Kunden nicht lesbar, nichts beendet: ${fehlerText(err)}`)
|
||||
}
|
||||
return { beendet }
|
||||
}
|
||||
|
||||
let tagesJobLaeuft = false
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user