Öffnungszeiten: Mails K24–K27 heißen jetzt K27–K30, Feld k24At heißt einschaltMailAt
K24–K26 sind seit dem Webshop-Livegang (main) vergeben; der Mail-Lader sucht die Vorlage über den Dateianfang, doppelte Nummern hätten die falsche Mail geschickt. Das Feld ist noch nicht in der Produktion angelegt (zeiten-setup.mjs lief noch nicht), daher jetzt ein sprechender Name. Spec und Plan behalten die alten Nummern (Hinweis oben im Dokument). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ea4457e6a4
commit
c7cd436ec2
34 files changed
+359
-354
No files matched your search
@@ -5,7 +5,7 @@ import { featureConfig, ticketTexte } from '../featureRegistry.js'
|
||||
import { withLocks } from '../keyedLock.js'
|
||||
import { customerOf, ensureFeatureDoc, getFeatureDoc, stateFields, ticketWoid, unlockedByVon } from '../siteFeatures.js'
|
||||
import { createFeatureTicket } from '../ticketAdmin.js'
|
||||
import { k24Mail, sendeZeitenMail } from './mails.js'
|
||||
import { k27Mail, sendeZeitenMail } from './mails.js'
|
||||
import { stosseNeubauAn } from './neubau.js'
|
||||
import { ladeZeiten } from './speicher.js'
|
||||
import { hauptplan, statusZu } from './zeitlogik.js'
|
||||
@@ -16,8 +16,8 @@ import { hauptplan, statusZu } from './zeitlogik.js'
|
||||
*
|
||||
* einschaltenZeiten Schritte 1-4 unter der Sperre `feature-order:<kunde>:<projekt>` (dieselbe wie im Checkout, damit sich
|
||||
* Bestellen und Einschalten ausschliessen): Abo frisch pruefen, Zustand pruefen (idempotent), Nachweis speichern,
|
||||
* Dokument auf setup. Danach, ohne diese Sperre: holeZeitenNach (Neubau, Ticket, K24).
|
||||
* holeZeitenNach Schritte 5 und 6, jeder fuer sich nur, wenn er noch fehlt: Neubau (einbau), Einbau-Ticket, K24, dann k24At.
|
||||
* Dokument auf setup. Danach, ohne diese Sperre: holeZeitenNach (Neubau, Ticket, K27).
|
||||
* holeZeitenNach Schritte 5 und 6, jeder fuer sich nur, wenn er noch fehlt: Neubau (einbau), Einbau-Ticket, K27, dann einschaltMailAt.
|
||||
* Eigene Sperre `zeiten-nachholen:<dokument>`: ein Doppelklick und der 5-Minuten-Job legen nie zwei Tickets an.
|
||||
*
|
||||
* Sperren (keyedLock) sind nicht wiederbetretbar: stosseNeubauAn nimmt seine eigene Sperre `zeiten-neubau`, darum laufen Neubau, Ticket
|
||||
@@ -47,8 +47,8 @@ function notizDesTeams(staff, bisherige, now) {
|
||||
* Oeffnungszeiten live fuer ein Projekt einschalten (Spec 3.2, Schritte 1 bis 6).
|
||||
* @param {{ customerId: string, projectId: string, by?: 'kunde'|'team', staff?: { name?: string, adminNote?: string }|null,
|
||||
* nachweis?: (() => Promise<unknown>)|null, now?: Date }} p
|
||||
* by 'kunde': Abo frisch pruefen (kein_abo/gekuendigt/nicht_aktiv 409, Lesefehler 503), unlockedBy 'abo', K24 geht an den Kunden.
|
||||
* by 'team': keine Abo-Pruefung, unlockedBy 'team' (ein bisher gekauftes Dokument bleibt kauf), keine K24; staff ergibt die Notiz am Dokument.
|
||||
* by 'kunde': Abo frisch pruefen (kein_abo/gekuendigt/nicht_aktiv 409, Lesefehler 503), unlockedBy 'abo', K27 geht an den Kunden.
|
||||
* by 'team': keine Abo-Pruefung, unlockedBy 'team' (ein bisher gekauftes Dokument bleibt kauf), keine K27; staff ergibt die Notiz am Dokument.
|
||||
* nachweis: speichert den Zustimmungsnachweis (Schritt 3, nur wenn wirklich eingeschaltet wird); wirft er, bleibt der Zustand unveraendert.
|
||||
* @returns {Promise<{ doc: object, changed: boolean }>} doc: das Dokument nach dem Nachholen; changed: false bei idempotentem Aufruf
|
||||
* @throws {Error} 409 (Abo, anderer Zustand), 503 (Abo nicht lesbar; oder Zustand gespeichert, aber Neubau/Ticket nicht fertig: erneut aufrufen)
|
||||
@@ -97,7 +97,7 @@ export async function einschaltenZeiten({ customerId, projectId, by = 'kunde', s
|
||||
return { doc: gespeichert, changed: true }
|
||||
})
|
||||
|
||||
// 5 und 6 nach der Sperre: Neubau, Ticket, K24
|
||||
// 5 und 6 nach der Sperre: Neubau, Ticket, K27
|
||||
try {
|
||||
return { doc: await holeZeitenNach(doc, { now }), changed }
|
||||
} catch (err) {
|
||||
@@ -112,11 +112,11 @@ export async function einschaltenZeiten({ customerId, projectId, by = 'kunde', s
|
||||
* vom Kauf stammt (unlockedBy kauf bringt markPaid in Ordnung):
|
||||
* 1. einbau leer => Neubau anstossen (stosseNeubauAn), Ergebnis in einbau
|
||||
* 2. ticketId leer => Einbau-Ticket aus dem Register (Text mit dem frischen einbau), ticketId
|
||||
* 3. unlockedBy abo und k24At leer => K24 an den Kunden, danach k24At. Gesendet wird erst nach dem Ticket (die Mail nennt es);
|
||||
* nicht gesendet (Fehler) laesst k24At leer, ein Kunde ohne Adresse oder ein geloeschter Kunde bekommt k24At (es gibt nichts zu senden).
|
||||
* 3. unlockedBy abo und einschaltMailAt leer => K27 an den Kunden, danach einschaltMailAt. Gesendet wird erst nach dem Ticket (die Mail nennt es);
|
||||
* nicht gesendet (Fehler) laesst einschaltMailAt leer, ein Kunde ohne Adresse oder ein geloeschter Kunde bekommt einschaltMailAt (es gibt nichts zu senden).
|
||||
* Jeder Schritt erst nach dem Erfolg des vorigen. Liest das Dokument unter der Sperre frisch; andere Zustaende bleiben unberuehrt.
|
||||
* Wirft bei Lesefehlern und wenn das Ticket nicht angelegt werden kann (der Aufrufer faengt und versucht es spaeter); eine
|
||||
* nicht gesendete K24 wirft nicht.
|
||||
* nicht gesendete K27 wirft nicht.
|
||||
* @param {{ $id: string }} doc portalFeatures-Dokument der Zeiten (nur $id zaehlt)
|
||||
* @param {{ now?: Date }} [options]
|
||||
* @returns {Promise<object>} das aktuelle Dokument
|
||||
@@ -126,9 +126,9 @@ export async function holeZeitenNach(doc, { now = new Date() } = {}) {
|
||||
return withLocks([`zeiten-nachholen:${doc.$id}`], async () => {
|
||||
let frisch = await getDocument(config.collections.portalFeatures, doc.$id)
|
||||
if (frisch.state !== 'setup' || unlockedByVon(frisch) === 'kauf') return frisch
|
||||
const mitK24 = unlockedByVon(frisch) === 'abo'
|
||||
const mitK27 = unlockedByVon(frisch) === 'abo'
|
||||
// nichts fehlt: weder Projekt noch Kunde lesen (der 5-Minuten-Job fragt jedes Dokument in setup)
|
||||
if (frisch.einbau && frisch.ticketId && !(mitK24 && !frisch.k24At)) return frisch
|
||||
if (frisch.einbau && frisch.ticketId && !(mitK27 && !frisch.einschaltMailAt)) return frisch
|
||||
const feature = featureConfig(KEY)
|
||||
// das Projekt frisch: der Neubau und der Text des Tickets brauchen den Stand nach dem Speichern des Zustands
|
||||
const project = await getDocument(config.collections.websiteProjects, frisch.projectId)
|
||||
@@ -145,8 +145,8 @@ export async function holeZeitenNach(doc, { now = new Date() } = {}) {
|
||||
frisch = await updateDocument(config.collections.portalFeatures, frisch.$id, { ticketId })
|
||||
}
|
||||
|
||||
if (mitK24 && !frisch.k24At) {
|
||||
const nachricht = k24Mail({
|
||||
if (mitK27 && !frisch.einschaltMailAt) {
|
||||
const nachricht = k27Mail({
|
||||
kunde: customer,
|
||||
projekt: project?.projectName || project?.subdomain || '',
|
||||
einbau: frisch.einbau,
|
||||
@@ -155,7 +155,7 @@ export async function holeZeitenNach(doc, { now = new Date() } = {}) {
|
||||
})
|
||||
const { sent, reason } = await sendeZeitenMail(customer, nachricht)
|
||||
if (sent || reason === 'keine_adresse') {
|
||||
frisch = await updateDocument(config.collections.portalFeatures, frisch.$id, { k24At: now.toISOString() })
|
||||
frisch = await updateDocument(config.collections.portalFeatures, frisch.$id, { einschaltMailAt: now.toISOString() })
|
||||
}
|
||||
}
|
||||
return frisch
|
||||
|
||||
Reference in new issue
Block a user