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>
73 lines
3.4 KiB
JavaScript
73 lines
3.4 KiB
JavaScript
/**
|
|
* Oeffnungszeiten live: Appwrite-Schema der beiden Collections und der neuen portalFeatures-Felder
|
|
* (Spezifikation 2026-10-04-oeffnungszeiten-live-design.md, Abschnitte 2.1 bis 2.3).
|
|
* Reine Daten, absichtlich ohne Import von server/config.js: test/support/schema.js liest sie vor useTestEnv(),
|
|
* ein frueh geladenes config.js wuerde den Endpunkt des Appwrite-Nachbaus verfehlen. Die Collection-IDs stehen in
|
|
* config.collections.<Name>; scripts/zeiten-setup.mjs legt alles an (auch die portalFeatures-Felder, bewusst nicht
|
|
* scripts/feature-setup.mjs: dessen Tests pinnen genau drei Attribute).
|
|
*
|
|
* Attribut: { key, type: 'string' | 'integer' | 'boolean' | 'datetime', size (nur Text), required: false }
|
|
* Index: { key, type: 'key', attributes: [...] }
|
|
* Datumswerte sind UTC (toISOString()); Appwrite liefert sie als "+00:00" zurueck, daher immer mit new Date() lesen.
|
|
*/
|
|
|
|
const str = (key, size) => ({ key, type: 'string', size, required: false })
|
|
const int = (key) => ({ key, type: 'integer', required: false })
|
|
const dt = (key) => ({ key, type: 'datetime', required: false })
|
|
const index = (key, ...attributes) => ({ key, type: 'key', attributes })
|
|
|
|
export const ZEITEN_COLLECTIONS = {
|
|
// Eine Einstellung je Funktion; Dokument-ID = ID des portalFeatures-Dokuments. Nur der Server-Key schreibt.
|
|
// daten = JSON der Kundeneingaben (hoechstens 32 KB, die Spalte ist groesser), team = JSON der Team-Einstellungen
|
|
// (nur ueber WOMS aenderbar), ersatzHash = Hash des Hauptplan-Wochenplans beim letzten Ersatz-HTML,
|
|
// updatedBy = 'kunde' | 'team:<staffId>'.
|
|
oeffnungszeiten: {
|
|
attributes: [
|
|
str('customerId', 64),
|
|
str('projectId', 64),
|
|
str('featureId', 64),
|
|
int('version'),
|
|
str('daten', 40000),
|
|
int('teamVersion'),
|
|
str('team', 2000),
|
|
str('ersatzHash', 64),
|
|
dt('updatedAt'),
|
|
str('updatedBy', 64),
|
|
],
|
|
// idx_customer: Loeschung beim Kunden-Loeschen
|
|
indexes: [index('idx_customer', 'customerId')],
|
|
},
|
|
|
|
// Erinnerungsmerker der Feiertags-Mail; Dokument-ID = sha1(featureId|datum) gekuerzt (anlegen mit 409 = schon
|
|
// erinnert). datum = Kalendertag 'YYYY-MM-DD' (Berliner Zeit); der Tagesjob loescht Merker, deren Datum > 30 Tage
|
|
// zurueckliegt (idx_datum); idx_feature: Loeschung der Merker einer Funktion (Kunden-Loeschen).
|
|
zeitenErinnerungen: {
|
|
attributes: [str('featureId', 64), str('datum', 10), dt('createdAt')],
|
|
indexes: [index('idx_datum', 'datum'), index('idx_feature', 'featureId')],
|
|
},
|
|
}
|
|
|
|
/**
|
|
* Neue Felder von portalFeatures (alle optional), angelegt von scripts/zeiten-setup.mjs:
|
|
* - unlockedBy: 'kauf' | 'abo' | 'team'
|
|
* - aboEndeAt: Zeitpunkt, an dem der Abgleich das Ende des Wartungs-Abos erkannt hat
|
|
* - einbau: 'angestossen' | 'manuell' (Ergebnis des Neubau-Anstosses)
|
|
* - behaltenSessionId: offene Behalten-Zahlung (Stripe-Sitzung)
|
|
* - einschaltMailAt: Marker der Mail K27 (Selbstheilung)
|
|
*/
|
|
export const ZEITEN_FEATURE_ATTRIBUTES = [
|
|
str('unlockedBy', 8),
|
|
dt('aboEndeAt'),
|
|
str('einbau', 12),
|
|
str('behaltenSessionId', 128),
|
|
dt('einschaltMailAt'),
|
|
]
|
|
|
|
/** Datumsfelder je Collection, damit der Appwrite-Nachbau der Tests sie wie Appwrite formatieren kann. */
|
|
export const ZEITEN_DATETIMES = Object.fromEntries(
|
|
Object.entries(ZEITEN_COLLECTIONS).map(([name, { attributes }]) => [
|
|
name,
|
|
attributes.filter((attribute) => attribute.type === 'datetime').map((attribute) => attribute.key),
|
|
]),
|
|
)
|