#!/usr/bin/env node /** * Kundenansicht neu (Paket P5, Abo-Modell Kern): legt die neuen Attribute an. Der Code schreibt sie nur, wenn ein * Aufrufer die zugehoerigen Parameter uebergibt; ohne dieses Skript bleibt die alte Ansicht unberuehrt. * - featureConsents.kundenart: Kundenart zum Klickzeitpunkt im Nachweis ('privat' | 'unternehmen') * - workorders.einrichtungFuer: Schluessel der Funktion, die das Einrichtungs-Ticket betrifft (laengster: 'unterkunft') * - workorders.einrichtungAbrechnung: 'abo' | 'projekt' | '' (Abrechnung der Einrichtung am Ticket) * - portalFeatures.einrichtungAbrechnung: dieselbe Abrechnung am Funktionsdokument (Nachlegen und Heilen setzen * den Marker am Ticket erneut) * - portalFeatures.unlockedBy: 'kauf' | 'abo' | 'team' | 'projekt' (steht schon im Zeiten-Schema; fehlt es, legt * dieses Skript es an) * Idempotent: Vorhandenes wird uebersprungen. Das Skript loescht nichts und laeuft in Entwicklung und Tests nie * gegen Produktion; dort nur auf Anweisung des Controllers nach OK des Inhabers, vorher immer mit --pruefen. * scripts/setup-abo-collections.mjs, feature-setup.mjs und zeiten-setup.mjs bleiben unveraendert. * Usage: * node scripts/kundenansicht-setup.mjs --pruefen (Alias --check; nur melden, nichts anlegen; Exit 1 bei Luecken) * node scripts/kundenansicht-setup.mjs (Fehlendes anlegen) * * config.js und der Appwrite-Client werden erst in main() geladen: test/support/schema.js importiert die * Attributlisten, bevor die Tests ihre Umgebung (useTestEnv) setzen koennen. */ import { pathToFileURL } from 'node:url' import { ZEITEN_FEATURE_ATTRIBUTES } from '../server/services/zeiten/schema.js' const str = (key, size) => ({ key, type: 'string', size, required: false }) const unlockedBy = ZEITEN_FEATURE_ATTRIBUTES.find((a) => a.key === 'unlockedBy') export const KUNDENANSICHT_ATTRIBUTE = { featureConsents: [str('kundenart', 12)], workorders: [str('einrichtungFuer', 16), str('einrichtungAbrechnung', 8)], portalFeatures: [unlockedBy, str('einrichtungAbrechnung', 8)], } export async function main({ pruefen = false, client, log = console.log } = {}) { const { config } = await import('../server/config.js') let api = client if (!api) { const { createAppwriteClient } = await import('./analyse-setup.mjs') api = createAppwriteClient() } const missing = [] const plan = [] // Erst alles pruefen, dann (ohne --pruefen) anlegen, damit --pruefen garantiert nichts veraendert for (const [name, attributes] of Object.entries(KUNDENANSICHT_ATTRIBUTE)) { const id = config.collections[name] const exists = await api.collectionExists(id) if (!exists && !pruefen) throw new Error(`Collection ${id} fehlt – zuerst das jeweilige Setup-Skript ausfuehren`) if (!exists) log(`- ${id} FEHLT`) const have = exists ? new Set((await api.listAttributes(id)).map((a) => a.key)) : new Set() for (const attr of attributes) { if (have.has(attr.key)) log(`= ${id}.${attr.key} (existiert)`) else if (pruefen) log(`- ${id}.${attr.key} FEHLT`) } const absent = attributes.filter((a) => !have.has(a.key)) absent.forEach((a) => missing.push({ ...a, collection: id })) plan.push({ id, absent }) } const result = { missing } if (pruefen) { log(missing.length ? `${missing.length} Eintrag/Eintraege fehlen.` : 'Alles vorhanden.') return result } for (const { id, absent } of plan) { for (const attr of absent) { await api.createAttribute(id, attr) log(`+ Attribut ${id}.${attr.key}`) } } for (const { id, absent } of plan) { for (const attr of absent) await api.waitForAttribute(id, attr.key) } log('Fertig.') return result } const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href if (invokedDirectly) { const pruefen = process.argv.includes('--pruefen') || process.argv.includes('--check') main({ pruefen }) .then(({ missing }) => process.exit(pruefen && missing.length ? 1 : 0)) .catch((e) => { console.error(e.message) process.exit(1) }) }