Kundenansicht P5: Setup-Skript für Kundenart und Einrichtungs-Marker

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-16 15:47:04 +00:00
1 parent c2b7073d99
commit c5b1333953
4 files changed
+171 -4

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
#!/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)
})
}