Files
Webklar-Kundenbereich/server/services/zeiten/abschalten.js
T
ClaudeandClaude Opus 5.5 97e6971276 Öffnungszeiten: Team-Aktionen und Ersatz-Ticket beim Abschalten
Team-Route für zeiten: Aktivieren läuft über den Gratis-Pfad (einschaltenZeiten, setup,
unlockedBy team, Neubau, Einbau-Ticket, keine K24), Abschalten setzt disabled mit Grund team
ohne Löschfrist und legt über nachAbschaltung bei geändertem Wochenplan das Ticket
"Ersatz-Tabelle aktualisieren" an. PATCH /:customerId/zeiten/team speichert die
Team-Einstellungen mit teamVersion. Die Team-Ansicht liefert für zeiten zusätzlich plaene,
team, teamVersion und hindernis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 02:39:43 +00:00

96 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { config } from '../../config.js'
import { berlinDateTime } from '../berlinTime.js'
import { getDocument } from '../appwriteAdmin.js'
import { createFeatureTicket } from '../ticketAdmin.js'
import { ersatzHtml, wochenplanHash } from './ersatz.js'
import { ladeZeiten, setzeErsatzHash } from './speicher.js'
import { hauptplan } from './zeitlogik.js'
/**
* Nach dem Abschalten von Oeffnungszeiten live (Spec 4.5): steht in den Platzhaltern der Kundenseite noch eine Ersatz-Tabelle, die
* nicht mehr zum aktuellen Wochenplan passt, bekommt das Team ein WOMS-Ticket "Ersatz-Tabelle aktualisieren - <Projekt>" mit dem
* aktuellen Ersatz-HTML (gehoert zum enthaltenen Einbau). Ohne Skript zeigt die Seite ab jetzt nur noch diese Tabelle.
*
* Aufrufer: die Team-Route (disable) und spaeter der Abo-Abgleich (Abo-Ende). Aufruf NACH dem Speichern des Zustands und ausserhalb
* jeder Sperre `feature-admin:<projekt>`: das Ticket legt eine Netzwerkanfrage an, und Sperren sind nicht wiederbetretbar.
*/
const THEMA = 'Ersatz-Tabelle aktualisieren'
/** Text des Tickets: Kopfzeilen wie bei den Einbau-Tickets, Anlass, Aufgabe und das aktuelle Ersatz-HTML des Hauptplans. */
function ticketDetails({ doc, project, daten, haupt, now }) {
const projektName = project?.projectName || project?.subdomain || ''
const domain = project?.liveDomain || project?.previewUrl || project?.subdomain || ''
return [
'Funktion: zeiten',
`Projekt: ${projektName}`,
`Domain: ${domain}`,
`siteKey: ${doc.siteKey ?? ''}`,
'',
`Öffnungszeiten live wurde am ${berlinDateTime(now).date} abgeschaltet. Der Wochenplan weicht von der Tabelle ab, die beim Einbau auf die Seite gesetzt wurde (oder der damalige Stand ist nicht bekannt).`,
'Aufgabe: Auf der Kundenseite in jedem Platzhalter <div data-webklar-zeiten ...> die Tabelle durch die aktuelle ersetzen. Ohne Skript zeigt die Seite nur noch diese Tabelle.',
'Weitere Pläne: WOMS → Funktionen → Ersatz-HTML kopieren.',
'',
haupt.kurz ? `Ersatz-HTML (Hauptplan ${haupt.kurz}):` : 'Ersatz-HTML (Hauptplan):',
ersatzHtml(daten, haupt),
].join('\n')
}
/** Das Projekt des Dokuments; ein geloeschtes Projekt (404) ist kein Fehler, der Name fehlt dann im Ticket. */
async function ladeProjekt(projectId) {
try {
return await getDocument(config.collections.websiteProjects, projectId)
} catch (err) {
if (err?.status === 404) return null
throw err
}
}
/**
* Pruefen, ob die Ersatz-Tabelle der Seite noch zum Wochenplan passt, und sonst das Ticket anlegen.
* Ruling R23: ein leerer oder fehlender gemerkter Hash (ersatzHash) gilt als "unbekannt", also wie geaendert, sobald Zeiten da sind
* (schlimmstenfalls ein unnoetiges Ticket). Nach dem Ticket wird der Hash auf den aktuellen Stand gesetzt: er gehoert dann zur Tabelle,
* die das Team einsetzt, und ein zweiter Aufruf (Doppelklick, spaeterer Lauf) legt kein zweites Ticket an. Scheitert das Ticket,
* bleibt der Hash, und der naechste Aufruf versucht es erneut.
* @param {{ $id: string, customerId?: string, projectId?: string, siteKey?: string }} doc portalFeatures-Dokument der Zeiten
* (aus disable(): der Stand nach dem Abschalten)
* @param {{ now?: Date }} [options]
* @returns {Promise<'angelegt'|'aktuell'|'fehler'>} angelegt: Ticket entstanden; aktuell: nichts zu tun (keine Zeiten oder Hash gleich);
* fehler: nicht pruefbar oder Ticket nicht angelegt (im Log ohne Kundendaten). Wirft nie.
*/
export async function nachAbschaltung(doc, { now = new Date() } = {}) {
try {
if (typeof doc?.$id !== 'string' || doc.$id === '') throw new TypeError('Dokument ohne $id')
const gespeichert = await ladeZeiten(doc.$id)
const daten = gespeichert?.daten ?? null
const haupt = hauptplan(daten)
// ohne Zeiten wurde nie eine Tabelle zum Ersetzen vorbereitet
if (!haupt) return 'aktuell'
const hash = wochenplanHash(daten)
if (gespeichert.ersatzHash !== '' && gespeichert.ersatzHash === hash) return 'aktuell'
const project = await ladeProjekt(doc.projectId)
const projektName = project?.projectName || project?.subdomain || ''
await createFeatureTicket({
customerId: doc.customerId,
project,
featureKey: 'zeiten',
siteKey: doc.siteKey,
topic: projektName ? `${THEMA} – ${projektName}` : THEMA,
details: ticketDetails({ doc, project, daten, haupt, now }),
})
// Das Ticket steht; scheitert nur das Merken, entsteht beim naechsten Abschalten ein zweites (Log, kein Fehler)
try {
await setzeErsatzHash(doc, hash)
} catch (err) {
console.error(`[zeiten] Ersatz-Hash für Projekt ${doc.projectId} nach dem Ticket nicht gemerkt: ${err?.name ?? 'Fehler'} ${err?.status ?? ''}`.trim())
}
return 'angelegt'
} catch (err) {
// nie Kundendaten ins Log: Art und Statuscode, keine Meldung (Appwrite-Meldungen koennen Feldinhalte nennen)
console.error(`[zeiten] Ersatz-Ticket nach dem Abschalten (Projekt ${doc?.projectId ?? '?'}): ${err?.name ?? 'Fehler'} ${err?.status ?? ''} ${err?.type ?? ''}`.replace(/\s+/g, ' ').trim())
return 'fehler'
}
}