/** * 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.; 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:'. 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) * - k24At: Marker der Mail K24 (Selbstheilung) */ export const ZEITEN_FEATURE_ATTRIBUTES = [ str('unlockedBy', 8), dt('aboEndeAt'), str('einbau', 12), str('behaltenSessionId', 128), dt('k24At'), ] /** 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), ]), )