Files
Webklar-Kundenbereich/scripts/kauf-bestand-pruefen.mjs
T

75 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
/**
* Vor dem Umschalten der Kundenansicht (P10): Käufe lesen. Nur lesend, schreibt nie.
*
* Nach dem Umschalten gibt es keine neuen Käufe mehr (die Kaufwege sind dann 404). Bestehendes muss weiter laufen: die Zahlung einer
* vorher gestarteten Bestellung (SEPA braucht Tage) und der Widerruf gekaufter Funktionen innerhalb der Frist. Das Skript zählt beides:
* offene Bestellungen state 'ordered' (die Zahlung steht noch aus); Exit 1, solange es welche gibt. Erst abwarten, dann umschalten.
* Fristen laufen gekaufte Funktionen (state setup | active | disabled und unlockedByPurchase), deren Widerrufsfrist noch läuft.
* Nur zur Information, kein Hindernis: der Widerruf funktioniert nach dem Umschalten weiter.
* Die Frist ist eine Näherung: Anker ist consentAt am Dokument, sonst orderedAt (der Nachweis in featureConsents wird nicht gelesen).
* Ohne Datum gilt die Frist vorsichtshalber als laufend.
*
* Ausgabe: nur Zahlen und Dokument-IDs, nie Namen, Adressen oder Beträge.
* node scripts/kauf-bestand-pruefen.mjs
* Gegen Produktion nur mit Freigabe des Inhabers (liest alle portalFeatures-Dokumente). Tests laufen nur gegen den Appwrite-Nachbau.
*/
import 'dotenv/config'
import { pathToFileURL } from 'node:url'
import { config } from '../server/config.js'
import { listAllDocuments } from '../server/services/appwriteAdmin.js'
import { withdrawalDeadline } from '../server/services/deadlines.js'
const GEKAUFT_STAENDE = ['setup', 'active', 'disabled']
const istGekauft = (doc) => GEKAUFT_STAENDE.includes(doc.state) && Boolean(doc.unlockedByPurchase)
/** Zeitpunkt des Vertragsschlusses (Näherung): Zustimmung am Dokument, sonst Bestellzeit; leer oder ungültig => null. */
function vertragAnker(doc) {
const wert = doc.consentAt || doc.orderedAt
if (!wert) return null
return Number.isNaN(new Date(wert).getTime()) ? null : new Date(wert)
}
/** @returns {{ laeuft: boolean, ohneDatum: boolean }} Läuft die Widerrufsfrist dieser gekauften Funktion noch? */
function fristStand(doc, jetzt) {
const anker = vertragAnker(doc)
if (!anker) return { laeuft: true, ohneDatum: true }
return { laeuft: withdrawalDeadline(anker).endsAt > jetzt, ohneDatum: false }
}
const liste = (ids) => (ids.length ? ` (${ids.join(', ')})` : '')
/**
* @param {{ log?: (line: string) => void, jetzt?: Date }} [options]
* @returns {Promise<{ offeneBestellungen: string[], fristLaeuft: string[], gekauftGesamt: number }>} nur Dokument-IDs
*/
export async function pruefeKaeufe({ log = console.log, jetzt = new Date() } = {}) {
const docs = await listAllDocuments(config.collections.portalFeatures)
const offeneBestellungen = docs.filter((d) => d.state === 'ordered').map((d) => d.$id)
const gekauft = docs.filter(istGekauft)
const fristLaeuft = []
const ohneDatum = []
for (const doc of gekauft) {
const stand = fristStand(doc, jetzt)
if (!stand.laeuft) continue
fristLaeuft.push(doc.$id)
if (stand.ohneDatum) ohneDatum.push(doc.$id)
}
log(`Funktionsdokumente: ${docs.length}, davon gekauft: ${gekauft.length}`)
log(`Offene Bestellungen: ${offeneBestellungen.length}${liste(offeneBestellungen)}`)
log(`Fristen laufen: ${fristLaeuft.length}${liste(fristLaeuft)}${ohneDatum.length ? ` - davon ohne Datum: ${ohneDatum.length}` : ''}`)
if (offeneBestellungen.length) log('Offene Bestellungen vorhanden: erst die Zahlung abwarten (oder klären), dann umschalten.')
return { offeneBestellungen, fristLaeuft, gekauftGesamt: gekauft.length }
}
if (import.meta.url === pathToFileURL(process.argv[1] || '').href) {
try {
const result = await pruefeKaeufe()
if (result.offeneBestellungen.length) process.exitCode = 1
} catch (err) {
console.error('Fehler:', err.message)
process.exit(1)
}
}