Öffnungszeiten: Bezahlen gesperrt, wenn im Abo enthalten
Checkout: neue Stufe 7b pruefeInklusive (Funktionen mit aboInklusive): laeuft das Wartungs-Abo (auch nicht aktiv), 409 inklusive mit Hinweis aufs Einschalten; kein Abo, Hosting und gekuendigt zahlen weiter 30 Euro. Zahlung (markPaid): setup mit unlockedBy kauf, dann Neubau (stosseNeubauAn, einbau am Dokument), erst danach das Einbau-Ticket mit dem Ergebnis; die Webhook-Wiederholung baut nicht noch einmal. Spaete Zahlungen aendern ein vorhandenes unlockedBy abo/team nicht. News und Terminplaner unveraendert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66793d06d5
commit
47ae572fff
4 files changed
+518
-4
No files matched your search
@@ -330,6 +330,26 @@ function notizVorn(previous, entry) {
|
||||
return aelter ? `${neu} | ${aelter}`.slice(0, NOTE_MAX) : neu
|
||||
}
|
||||
|
||||
/**
|
||||
* Funktionen mit aboInklusive (Oeffnungszeiten live) halten am Dokument fest, wodurch sie freigeschaltet wurden (unlockedBy) und was der
|
||||
* Neubau fuer den Einbau ergab (einbau). Fuer die uebrigen Funktionen (News, Terminplaner) schreibt der Kaufweg beides nicht: das Schema
|
||||
* dieser Felder gibt es nur, wo zeiten-setup.mjs gelaufen ist.
|
||||
*/
|
||||
const mitAbo = (key) => featureConfig(key)?.aboInklusive === true
|
||||
|
||||
/**
|
||||
* Neubau fuer den Einbau des Zeiten-Skripts (Spec 4.6) beim Kauf, VOR dem Einbau-Ticket: das Ticket nennt das Ergebnis (einbau).
|
||||
* Steht einbau schon am Dokument (Wiederholung des Webhooks nach einem gescheiterten Ticket), wird nicht noch einmal gebaut.
|
||||
* stosseNeubauAn nimmt die eigene Sperre 'zeiten-neubau' (andere Schluessel als feature-paid, also kein Wiederbetreten) und wirft nie;
|
||||
* zeiten/* nur per dynamischem Import (siehe Kopf). Liefert das Dokument mit dem gespeicherten einbau.
|
||||
*/
|
||||
async function baueZeitenEin(doc, project) {
|
||||
if (doc.einbau) return doc
|
||||
const { stosseNeubauAn } = await import('./zeiten/neubau.js')
|
||||
const einbau = await stosseNeubauAn(project)
|
||||
return updateDocument(config.collections.portalFeatures, doc.$id, { einbau })
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Zahlung trifft ein Dokument, das nicht (mehr) auf die Bezahlung wartet: active, disabled oder setup mit Ticket. Sie wird
|
||||
* einmalig verbucht (purchaseStatus 'paid', Rechnung, Notiz) und der Fall im eigenen Feld zahlungMeldung vermerkt - in einem einzigen
|
||||
@@ -343,6 +363,9 @@ async function bucheSpaeteZahlung(doc, { sessionId, invoiceId, invoiceUrl }) {
|
||||
const updated = await updateDocument(config.collections.portalFeatures, doc.$id, {
|
||||
purchaseStatus: 'paid',
|
||||
unlockedByPurchase: true,
|
||||
// Eine Zahlung, die nichts einrichtet, aendert nicht, wodurch das Dokument freigeschaltet wurde: ein abo oder team bleibt (der
|
||||
// Abo-Abgleich und die Loeschfrist haengen an unlockedBy abo); nur ohne Angabe (Altbestand) wird es zu kauf, passend zu unlockedByPurchase
|
||||
...(mitAbo(doc.featureKey) && !doc.unlockedBy ? { unlockedBy: 'kauf' } : {}),
|
||||
stripeInvoiceId: invoiceId || '',
|
||||
invoiceUrl: invoiceUrl || '',
|
||||
adminNote: notizVorn(doc.adminNote, `${new Date().toISOString()} System: Zahlung eingegangen (Sitzung ${sessionId}), nichts eingerichtet`),
|
||||
@@ -356,6 +379,9 @@ async function bucheSpaeteZahlung(doc, { sessionId, invoiceId, invoiceUrl }) {
|
||||
* wird auf 'setup' gestellt, dann das Ticket erstellt; scheitert das, bleibt 'setup' ohne ticketId
|
||||
* (das Portal zeigt "Ticket fehlt") und der Fehler wird weitergereicht. Stripe wiederholt den Webhook:
|
||||
* 'setup' ohne ticketId gilt dann als fortsetzbar (Ticket anlegen, created:true -> K15/T16 gehen raus).
|
||||
* Funktionen mit aboInklusive (Oeffnungszeiten live): 'setup' traegt unlockedBy 'kauf' (und einbau leer), danach stoesst baueZeitenEin den
|
||||
* Neubau an und speichert einbau, erst dann entsteht das Ticket mit dem Ergebnis. Fortsetzbar ist dort nur setup mit unlockedBy kauf; ein
|
||||
* per Abo oder Team eingeschaltetes Dokument ohne Ticket heilt holeZeitenNach, die Zahlung ist fuer es ein Fall (bereits_aktiv).
|
||||
*
|
||||
* Liefert { doc, created, fall }. fall ist null, ausser die Zahlung traf ein Dokument, das nicht mehr bestellt ist ('active',
|
||||
* 'disabled', 'setup' mit Ticket): dann wird sie einmalig verbucht und 'bereits_aktiv' | 'abgeschaltet' | 'abo_ende' geliefert,
|
||||
@@ -368,20 +394,27 @@ export async function markPaid({ sessionId, invoiceId, invoiceUrl }) {
|
||||
const doc = await findBySession(sessionId)
|
||||
if (!doc) return { doc: null, created: false, fall: null }
|
||||
if (doc.state === 'off') return { doc, created: false, fall: null }
|
||||
const resumable = doc.state === 'setup' && !doc.ticketId
|
||||
const mitAboKauf = mitAbo(doc.featureKey)
|
||||
// Funktionen mit aboInklusive: nur ein Dokument, das der Kauf auf setup gestellt hat (unlockedBy kauf), ist fortsetzbar. Eines, das per Abo
|
||||
// oder vom Team auf setup steht, bekommt sein Ticket vom Nachholen (holeZeitenNach); die Zahlung ist dort ein Fall fuers Team
|
||||
const resumable = doc.state === 'setup' && !doc.ticketId && (!mitAboKauf || unlockedByVon(doc) === 'kauf')
|
||||
if (doc.state !== 'ordered' && !resumable) return bucheSpaeteZahlung(doc, { sessionId, invoiceId, invoiceUrl })
|
||||
|
||||
const setup = resumable
|
||||
let setup = resumable
|
||||
? doc
|
||||
: await updateDocument(config.collections.portalFeatures, doc.$id, {
|
||||
...stateFields('setup'),
|
||||
purchaseStatus: 'paid',
|
||||
unlockedByPurchase: true,
|
||||
// einbau beginnt leer: Reste eines frueheren Laufs gelten nicht, der Neubau (baueZeitenEin) setzt es neu
|
||||
...(mitAboKauf ? { unlockedBy: 'kauf', einbau: '' } : {}),
|
||||
stripeInvoiceId: invoiceId || '',
|
||||
invoiceUrl: invoiceUrl || '',
|
||||
zahlungMeldung: '',
|
||||
})
|
||||
const project = await getDocument(config.collections.websiteProjects, doc.projectId)
|
||||
// Oeffnungszeiten live: erst der Neubau (Zustand ist schon gespeichert), dann das Ticket mit dem frischen einbau (Spec 4.6)
|
||||
if (mitAboKauf) setup = await baueZeitenEin(setup, project)
|
||||
const customer = await customerOf(doc.customerId).catch(() => null)
|
||||
const feature = featureConfig(doc.featureKey)
|
||||
const texte = feature ? await ticketTexte({ feature, doc: setup, project, customer }) : {}
|
||||
|
||||
Reference in new issue
Block a user