Commit Graph
51 Commits
Author SHA1 Message Date
ClaudeandClaude Opus 5.5 e87140e5ba Merge remote-tracking branch 'origin/main' into feature/oeffnungszeiten
Zusammenführung von TP2-Abschluss und Webshop (origin/main cab9c33) mit Öffnungszeiten live. Beide
Funktionen bleiben vollständig: jeder Konflikt ist als Vereinigung gelöst, Reihenfolge der Funktionsschlüssel
überall news, termine, shop, zeiten.

Konfliktdateien (28): public/app.js, dashboard.html, funktionen.js, widerrufen.html; scripts/stripe-products.mjs;
server/config.js, index.js, middleware/kundenansichtGuard.js, routes/abo.js, routes/admin/funktionenAdmin.js,
routes/funktionen.js, services/customerAdmin.js, customerViewLog.js, featureRegistry.js, retention.js,
siteFeatures.js, stripeBilling.js; test/customerViewLog, featureFreischaltung, featureRegistry, funktionenRoutes,
funktionenTermine, kundenansichtGuard, mailTemplates, publicForms, siteFeatures, stripeProductsScript (je
.test.js) und test/support/schema.js.

Entscheidungen: Mailvorlagen G01–G15, K01–K30, T01–T21 lückenlos (K24–K26 Webshop, K27–K30 Öffnungszeiten);
Stripe-Katalog mit shopSetup und zeitenSetup; Widerruf kennt shop und zeiten; ABO_FUNKTIONEN bleibt analyse
und termine (kein zeiten); activate() schreibt die Zeiten-Felder nur über extra; kein Halter von
feature-activate:<id> ruft disable() auf; die Aufbewahrungs-Löschung löscht Zeiten-Daten vor dem Abschalten und
hält den Kunden bei einem Fehler (held); die Admin-Antwort trägt ersatzTicket und warnung; Hash-Routen webshop
und zeiten, Ansichten view-webshop und view-zeiten; assertOwnProject behält die Meldung von main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 06:12:23 +00:00
Claude a4a18a1c89 Merge branch 'feature/terminplaner-tp2' into feature/webshop 2026-10-05 14:56:43 +00:00
ClaudeandClaude Sonnet 5.5 2c7cd26532 Termine-Outbox: notEqual mit genau einem Wert (Appwrite 1.8.1), nachrangige Mails kommen wieder dran
Auf der echten Appwrite 1.8.1 lehnt notEqual mit Liste die Abfrage ab (HTTP 400
general_query_invalid, "NotEqual queries require exactly one value"). Die Klasse normal der
Mail-Warteschlange schickte immer eine Liste, jeder Lauf warf dort, und die nachrangige Klasse
(G05, K20, K21) kam nie dran.

- appwriteAdmin: Query.notEqual nimmt genau einen Wert (eine Liste wirft einen TypeError, der
  keinerVon nennt); neu keinerVon(attribute, werte) = je Wert ein notEqual (Appwrite schneidet
  sie); dokumenteUrl() baut die Listen-URL ohne Netz, mit derselben Serialisierung wie die Anfrage.
- outbox: klassenFilter liefert fuer jede Klasse eine Liste von Abfragen (normal = sechs notEqual
  auf art), die Ausschlussliste ist keinerVon('featureId', ...). AUSSCHLUSS_MAX 90 -> 30 (der nginx
  antwortet in dieser Abfrageform ab 46 Funktions-IDs mit 414); der Kommentar "100 Werte je Abfrage"
  entfaellt.
- Nachbau (appwriteMock): notEqual mit Liste oder ohne Wert => 400 wie Appwrite; ein Dokument mit
  Feld null oder ohne Feld passt nicht zu notEqual (SQL-Semantik).
- Tests: Query-Helfer, keinerVon, dokumenteUrl, Nachbau (400, null), ein Lauf bedient alle drei
  Klassen, Ausschlussliste hoechstens 30 und URL unter 6000 Zeichen im schlimmsten Fall.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 14:47:29 +00:00
ClaudeandClaude Opus 5.5 7464a4ef55 Shop D3: Connect-Webhook (account.updated, Checkout-Abschluss/Verwerfen, charge.refunded nachtragen)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:04:47 +00:00
ClaudeandClaude Opus 5.5 bf99f659eb Shop E2: Abschluss (schliesseAb), verwirf, Abgleich offener Bestellungen, Minutenjob
schliesseAb ist die einzige Stelle fuer den Status bezahlt: Sperre je Bestellung, Konto muss zum Shop passen,
idempotent, spaete Zahlung nach Ablauf wird angenommen, SEPA erst bei bezahlt, Bestandsabzug mit Ueberverkauft-Hinweis,
G09 an den Kaeufer und K25 an den Haendler (Mailfehler machen nichts rueckgaengig). pruefeOffene und jobs.js minutenJob
gleichen offene Bestellungen mit Stripe ab. fakeStripe: completeSession.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 09:01:04 +00:00
ClaudeandClaude Opus 5.5 da43ee2b41 Shop D1: Connect-Dienst (Konto, Onboarding, Checkout, Erstattung, Webhook-Prüfung) und Fake-Stripe für Connect
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:44:10 +00:00
ClaudeandClaude Opus 5.5 e9ab15bdb5 Webshop A1/A2: Konfiguration, Schalter, Katalog shopSetup, Schema und Setup-Skript
config.shop (SHOP_ENABLED, SHOP_PILOT_EMAILS, SHOP_UPLOAD_DIR, STRIPE_CONNECT_WEBHOOK_SECRET), config.shopSetup (299 € einmalig),
Collection-IDs shopConfig/shopProducts/shopOrders/shopWiderrufe, services/shop/schalter.js, Katalogschluessel shopSetup in
stripeBilling und stripe-products.mjs, services/shop/schema.js, scripts/shop-setup.mjs (--check), schemaKeys fuer die Test-Mocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:30:21 +00:00
ClaudeandClaude Sonnet 5.5 5f99f08a39 TP2 Abschlussprüfung: Fragebogen und Bestellseite (veralteter Tab, Abo-Hinweis, Zurück aus Stripe)
- PUT /fragebogen: ein Tab mit veralteter Branche bekommt 409 EINSTELLUNG_VERALTET (die Branche wechselt nur die Vorlage), bestaetigt wird mit
  dem gespeicherten Stand vereinigt; die ungeprüften Vorlagenpreise können nicht mehr als geprüft gelten
- GET /einstellungen liefert in off aboHinweis (aboMeldung, auch bei nicht lesbarem Abo); die Hinweiskarte zeigt ihn immer und hat „Neu laden“
- Bestellseite und Statuskarte laden bei pageshow (persisted) neu, wenn der Browser sie nach der Zahlungsseite aus dem bfcache holt
- renderBestellen zeichnet „Ändern“ und „Zum Schritt“ nur mit zumSchritt; nach einem 409 bekommt die Meldung den Fokus
- Zusammenfassung nennt „Kleinunternehmer: ja/nein“

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 06:35:42 +00:00
ClaudeandClaude Opus 5.5 1d5a565e9f Merge feature/terminplaner-tp2 (a629b25) in feature/oeffnungszeiten
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 04:42:31 +00:00
ClaudeandClaude Opus 5.5 4b4cd1058f Öffnungszeiten: Schalter, Collections und Felder
ZEITEN_ENABLED/ZEITEN_PILOT_EMAILS/ZEITEN_KEIN_NEUBAU als config.zeiten (neben config.termine),
zeitenSetup (30 € einmalig), Collection-IDs oeffnungszeiten und zeitenErinnerungen, Schema der beiden
Collections plus fünf neue portalFeatures-Felder, scripts/zeiten-setup.mjs (idempotent, --check),
schemaKeys kennt die neuen Collections und Felder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:25:37 +00:00
ClaudeandClaude Sonnet 5.5 87d267e51a Terminplaner TP2: Ansicht nach Zustand und Rueckkehr von Stripe
Die Ansicht #termine folgt dem Zustand aus GET /api/termine/einstellungen:
off mit laufendem Abo zeigt den Fragebogen samt Bestellseite, ohne Abo den
Hinweis des Servers (termine.hinweis, R111) und bei kein_abo den Link zu
Vertraege aus einer gemeinsamen Quelle (vertraege-link.js); ordered und setup
zeigen die Statuskarte und die Reiter Leistungen, Team & Plaetze und
Einstellungen nur zum Lesen (ohne Reiter Termine); active und disabled
bleiben wie bisher. Rueckkehr von Stripe: #termine?bestellt=1&projekt=<id>
und #termine?projekt=<id> waehlen das Projekt, "Danke!" nur mit bestellt=1,
die Adresse wird einmal gelesen und bereinigt.

R110: GET /einstellungen liefert bestellung und offen auch in ordered (ohne
Abo-Punkt, abo bleibt null); scheitert "Zahlung fortsetzen", zeichnet die
Huelle Liste und Daten neu (bei 409 zustimmung_fehlt mit dem
Bestellformular, sonst nach dem neuen Zustand, die Meldung bleibt am Knopf,
solange die Bestellung offen ist).

Dazu: "null" als Text im lesbaren Reiter Leistungen entfernt, Export und
Status der Einstellungen erst ab der Freischaltung.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:10:54 +00:00
ClaudeandClaude Sonnet 5.5 747be432c5 Terminplaner TP2: Reiter-Exporte und wochenplanFeld fuer den Fragebogen
- termine-tab-einstellungen.js: die acht Abschnitte (betrieb, darstellung, regeln,
  gastFelder, erinnerung, storno, rechtliches, zahlung) sind Exporte; betrieb nimmt
  { nur, titel, hinweis } (nur die gewaehlten Felder, lese schreibt nur diese)
- termine-tab-team.js: wochenplanFeld(r, pfad) => { el, lese() => { plan } | { fehler } },
  der Editor einer Person benutzt es selbst
- termine-speichern.js: fehlt kommt aus der Antwort des Servers
- termine.js: baueCtx aus zeigeReiter herausgeloest (Verhalten gleich)
- test/support/fakeDom.js: Minimal-DOM und Fake-fetch fuer die Client-Tests

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 21:06:42 +00:00
ClaudeandClaude Sonnet 5.5 008ad4fb1e Terminplaner TP2: Bestell-Gate und Gesundheits-Stichwortpruefung
fragebogen.js ersetzt den Stub aus Block B: bestellpruefung (Kriterien (a) bis (h),
je Punkt ein Eintrag, nach Schrittfolge sortiert, Abo-Punkt nur bei mitAbo !== false),
SCHRITT_TITEL, gesundheitsWarnungen (Stichworte nur am Wortanfang, hoechstens 10
Meldungen) und bestellungAnsicht fuer die Bestellseite. einstellungen.js exportiert
sichtbareLeistungen und MELDUNG_PFLICHT. Testhelfer bestellbarerStand und entwurfDoc;
der Stub-Test entfaellt, die zwei B-Tests mit dem Stub laufen gegen das echte Gate.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 19:00:12 +00:00
ClaudeandClaude Sonnet 5.5 d8e4d79891 Schema: Zustimmungsnachweis featureConsents im Skript feature-setup
scripts/feature-setup.mjs legt neben den portalFeatures-Attributen die Collection
featureConsents (nur Server-Key, ohne Rechte) mit 11 Attributen und den Indizes
idx_session und idx_project_feature an; --check meldet je fehlende Collection,
Attribut und Index eine Zeile "- <id>[.<key>] FEHLT", schreibt nichts und liefert
Exit-Code 1 bei Luecken. config.collections.featureConsents (Env
APPWRITE_COLLECTION_FEATURE_CONSENTS) und schemaKeys('featureConsents') in
test/support/schema.js ziehen mit. setup-abo-collections.mjs bleibt unveraendert.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 11:57:16 +00:00
ClaudeandClaude Sonnet 5.5 aaed273b62 Funktionen: disable() idempotent, disabledReason, Schema-Skript feature-setup
disable() laeuft unter einer Sperre je Dokument und aendert ein schon
abgeschaltetes Dokument nicht mehr (disabledAt als Beginn der 30-Tage-Frist,
Grund und Notiz bleiben); nur Altbestand ohne disabledAt bekommt dieses Feld.
Neu: DISABLED_REASONS und der Parameter reason (Vorgabe team, unbekannt = 400),
disableCustomerFeatures reicht ihn durch, die Kundenloeschung nutzt
kunde_geloescht. activate() leert zusaetzlich disabledReason und aboEndeMailAt
und laesst zahlungMeldung unberuehrt.

scripts/feature-setup.mjs legt portalFeatures.disabledReason, aboEndeMailAt und
zahlungMeldung idempotent an (--check meldet Luecken mit Exit 1);
schemaKeys('portalFeatures') zieht die Schluessel mit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 11:33:17 +00:00
ClaudeandClaude Sonnet 5.5 2dd9c0dec7 Appwrite-Abfragen: select, notEqual, countDocuments und Nachbau dazu
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 08:38:21 +00:00
ClaudeandClaude Opus 5.5 10b393687c Mitarbeiter-Zugang: Review-Punkte (fail closed, Kunden-Konto bleibt, Probelauf robuster)
- removeEmployeeEntry: nur 404 heisst "kein Konto", jeder andere Fehler bricht ab
- Mitarbeiter loeschen (Admin-Panel): Konto ohne Mitarbeiter-Label (Kunde mit
  altem employees-Eintrag) bleibt bestehen, nur der Eintrag geht
- Mitarbeiter bearbeiten gibt nie automatisch das Label (der Eintrag kann von
  einem Kunden stammen); nur Neuanlage bekommt "mitarbeiter"
- --rechte: --offen-lassen <ids>, unlesbare Listen als Fehler statt Abbruch
  (--anwenden verweigert dann), --sicherung <datei> + docker-cp-Hinweis,
  Bucket-Einstellung transformations wird mitgeschickt
- getEmployeeByEmail entfernt (unbenutzt)
- Tests: write/users-unverified, zweiter Lauf idempotent, >100 Dokumente,
  Loeschen mit Kunden- und Mitarbeiter-Konto

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 11:00:48 +00:00
ClaudeandClaude Opus 5.5 2659385205 Mitarbeiter nur noch per Label: Kunden-Konten bekommen keinen Team-Zugang
Kunden haben Konten im selben Appwrite-Projekt wie das Ticketsystem. Bisher
galt als Mitarbeiter, wer einen employees-Eintrag hatte - den legte das
Ticketsystem beim Login jedes Kontos automatisch an, und jedes angemeldete
Konto konnte ihn auch selbst anlegen. Damit kam ein Kunde ueber /api/team an
Chats, Kundenansicht, Angebote und Finanzen.

- staffLabels.js: Mitarbeiter = Label "mitarbeiter" oder "admin" (setzt nur
  der Server-Key)
- Team-Eingang (requireStaffJwt) und Portal-Login-Sperre nur noch per Label;
  kein Abgleich ueber employees-Eintrag oder Mail mehr
- Portal-Login: Konto-Abruf gestoert -> 503 fuer alle (fail closed)
- Mitarbeiter anlegen/bearbeiten setzt das Label "mitarbeiter"
- scripts/mitarbeiter-zugang.mjs: --status (markiert employees-Eintraege von
  Kunden), --label, --eintrag-loeschen (nur Eintrag, nie das Konto), --rechte
  (Probelauf, --anwenden mit Sicherung, --zurueck): any/guests/users ->
  label:mitarbeiter + label:admin in Collections, Buckets und bei Dokument-/
  Datei-Sicherheit auch einzelnen Dokumenten und Dateien

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:54:09 +00:00
ClaudeandClaude Sonnet 5.5 4465ac7b94 Terminplaner: Einbau-Skript – Nachbesserungen
429 ohne Schleife (Meldung und Erneut-versuchen-Knopf, Formulare bleiben stehen), Schriftfarbe
auf der Betriebsfarbe mit mindestens 4,5 : 1, AGB-Link aus der oeffentlichen Konfiguration
(betrieb.agbUrl), Haken starten bei geaenderter Auswahl ohne Haken, Fehler ohne Feld nicht
verschluckt, Konfiguration fail closed, ladeNeu fuer Neuladen und neu_laden, Tastatur und
Fokus (Modifikatoren, Monatspfeile, Belehrungstext), Aufraeumen. UI-Testserver: Timer,
offene Verbindungen, Hinweis auf sessionCookie.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 08:17:44 +00:00
ClaudeandClaude Sonnet 5.5 a5e12d7f34 Appwrite: Bulk-Create, Bereichsabfragen, seitenweises Laden, genauerer Nachbau
appwriteAdmin: adminFetch kennt die Option raw (Body unverpackt), createDocuments
(Bulk-Create, alles oder nichts, 409 bei vorhandener ID), listAllDocuments (seitenweise
per cursorAfter, sortiert nach $id, Fehler ueber max) und Query.between/greaterThanEqual/
lessThanEqual/cursorAfter.

Nachbau: Bulk-Create mit Alles-oder-nichts (409/400, hoechstens 100), die neuen Abfragen
samt cursorAfter, Zeitpunktvergleich fuer ISO-Datumswerte, Optionen datetimes (Normalisierung
auf +00:00, leeres Datum => 400) und defaultLimit (Standard-Limit wie Appwrite). Ohne die
Optionen unveraendert. testApp exportiert send(method, path, opts).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 08:17:43 +00:00
ClaudeandClaude Opus 5.5 334dc1e669 Terminplaner: Schalter, Konfiguration, Schema
Schalter TERMINE_ENABLED (off|pilot|on) mit TERMINE_PILOT_EMAILS, config.termine
und die vier Collection-IDs, Schema der Termine-Collections als reine Daten
(server/services/termine/schema.js), Setup-Skript scripts/termine-setup.mjs mit
--check und schemaKeys() fuer den Appwrite-Nachbau der Tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:17:43 +00:00
ClaudeandClaude Opus 5.5 ff7fe35c06 Rechnungen: Stripe-Rechnungen im Kundenportal anzeigen (Abo, Funktionen, Projektrechnungen)
Der Tab "Rechnungen" las nur InvoiceNinja. Seit dem Finanzen-Livegang laufen Rechnungen
aber ueber Stripe; die bezahlte Abo-Rechnung eines Kunden fehlte deshalb im Portal.

- /api/invoices fuehrt Stripe (alle Stripe-Kunden des Kunden: Abo-Spiegel und Suche nach
  metadata.webklarCustomerId) und InvoiceNinja zusammen, neueste zuerst; Entwuerfe und
  0-EUR-Rechnungen bleiben verborgen. Faellt eine Quelle aus, kommt der Rest mit incomplete.
- /api/invoices/:id/pdf laedt Stripe-PDFs frisch ueber den Server, nur fuer eigene Rechnungen
  (sonst 404) und nur von *.stripe.com.
- Seite: Status-Farben fuer Bezahlt/Offen/Ueberfaellig/Storniert, "Ansehen" statt
  "Ansehen / Zahlen" bei bezahlten Rechnungen, Hinweis bei unvollstaendiger Liste; der
  Abo-Hinweis verweist nur noch fuer die Zahlungsart auf "Abo verwalten & Rechnungen".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:33:22 +00:00
ClaudeandClaude Opus 5.5 7c00633db0 Portal: Admin-Panel, Rechnungsanalyse und alte Admin-Schnittstellen entfernt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 03:50:14 +00:00
ClaudeandClaude Opus 5.5 3a4fa581c0 Portal: Mitarbeiter-Login gesperrt (Hinweis aufs Ticketsystem), alte Mitarbeiter-Sitzungen enden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 03:44:10 +00:00
ClaudeandClaude Opus 5.5 1cd776a2ee Kundenansicht: Einmal-Link aus dem Ticketsystem, Sitzung und Preview-Zugang fuer 2 Stunden, Protokoll
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 19:27:21 +00:00
ClaudeandClaude Opus 5.5 772095ab19 Team-Eingang: /api/team mit Appwrite-JWT fuer alle Mitarbeiter, CORS-Header fuer Uploads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 19:23:57 +00:00
ClaudeandClaude Opus 5.5 3d05472b25 Analyse: Einwilligungs-Historie, Nachweis beim Einschalten, Sperre ohne Verantwortlichen-Angaben, Textform für Mitarbeiter
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 18:50:36 +00:00
ClaudeandClaude Sonnet 5.5 f0b57ebe41 Finanzen: Nachtrag vor Livegang (Kundenabgleich beim Senden, Adress-Sperre, Land und Anschrift sauber an Stripe)
- sendInvoiceNow: Entwurf mit webklarCustomerId laedt den WOMS-Kunden und bringt den Stripe-Kunden vor dem
  Finalisieren auf die aktuellen Daten (Fehler: 502, nichts finalisiert); ohne Kennung kein Abgleich
- Adress-Sperre (§ 14 Abs. 4 Nr. 1 UStG): Senden ueber 250 € ohne Strasse/PLZ/Ort -> 409 vor jedem Stripe-Schreibzugriff
  (Anlegen mit senden=true und Entwurf senden; Text und Grenze wie im WOMS)
- stripeCustomerFields: Land aus deutschen Namen (unbekannt -> weglassen), PLZ/Ort mit Strasse immer mitsenden ('' loescht)
- Stripe-Nachbau: Kunden-Update fuehrt die Anschrift feldweise zusammen, '' loescht ein Feld

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 73811e0300 Finanzen: Stripe-Kunde vor der Rechnung auf die aktuellen Kundendaten bringen
Ein wiederverwendeter Stripe-Kunde (ueber das Abo oder die Metadaten gefunden) trug
noch die alte E-Mail und Anschrift, die Stripe auf die Rechnung schreibt
(Empfaengeranschrift, § 14 Abs. 4 Nr. 1 UStG). createProjectInvoice aktualisiert ihn
jetzt nach getOrCreateStripeCustomer und vor dem ersten Rechnungsaufruf mit E-Mail,
Name (Firma vor Name) und Anschrift (Land DE, wenn keine zweistellige Angabe).
Scheitert das, entsteht keine Rechnung: 502 "Kundendaten konnten nicht an Stripe
uebertragen werden." ohne invoiceId. Die Felder baut stripeCustomerFields fuer das
Anlegen und das Aktualisieren gemeinsam. Der Stripe-Nachbau kennt POST /v1/customers/:id.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 daa451094a Finanzen: Zeitgrenzen fuer Stripe-Lesen, Schutz vor doppelt angelegten Rechnungen
I2: Leseanfragen an Stripe mit timeout 15 s und einer Wiederholung (auch Folgeseiten), die Uebersicht
wartet hoechstens 20 s auf Stripe und rechnet sonst ohne Stripe weiter.
I3: idempotencyKey vom Aufrufer an stripe.invoices.create, Schutz im Prozess gegen gleichzeitiges und
30 s spaeteres Doppelanlegen (409), 502 mit invoiceId/Nummer wenn die fertige Rechnung nicht lesbar ist.
M9: Warnung, wenn die Rechnungsliste bei 1000 abgeschnitten wird.
M11: Ursache eines 502 an einer bestehenden Rechnung (Art, Status, Code) im Protokoll.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 6e43d54fe8 Finanzen: Zuordnung zu Rechnungen erst vermerken, dann bezahlen; Vormerkungen nicht zuordnen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 05e3478229 Finanzen: Projektrechnungen in Stripe anlegen, senden, stornieren, Entwurf loeschen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 db243f50bb Finanzen: Stripe-Rechnungen und Auszahlungen lesen (deutsch gemappt, 60-s-Cache)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:42:06 +00:00
ClaudeandClaude Sonnet 5.5 cba14ed0f0 Finanzen: Bankzugang aus WOMS neu verbinden (Freigabe, Rueckkehr, Kontowahl)
startBankAuth/completeBankAuth in bankSync.js. Die Rueckkehr prueft und speichert die
Sitzung unter der Sperre der Abrufe (ein laufender Abruf kann den neuen Zugang nicht
mehr ueberschreiben) und ruft danach einmal ab. Nachbau: holdNext haelt einen Aufruf an.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:41:54 +00:00
ClaudeandClaude Sonnet 5.5 3dd2fac50b Finanzen: Collections und Setup-Skript mit Sitzungsuebernahme
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:41:54 +00:00
ClaudeandClaude Sonnet 5.5 9329318df5 Finanzen: Enable-Banking-Client (JWT, Freigabe, Sitzung, Salden, Umsaetze)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-01 15:41:54 +00:00
knsoandClaude Opus 5.5 4b7f73abba Funktionen/News: Schlussdurchgang (Webhook-Retry, Checkout-Sitzung, Rechnungslink, Widerruf Funktion, UI- und Rechtstext-Korrekturen)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:38:15 +00:00
ClaudeandClaude Opus 5.5 d7c2027d3f 2FA-Login: Prüfung schlägt nicht offen fehl, Fehlversuche vor dem Aufruf zählen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 10:39:24 +00:00
ClaudeandClaude Opus 5.5 23d78815f9 Portal-Login: Appwrite-Sitzung merken, MFA-Aufrufe im Namen der Sitzung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 10:27:05 +00:00
ClaudeandClaude Opus 5.5 e4efdac226 Hosting-Abo: Preis 15 € -> 20 € pro Monat (Vertragsbedingungen v4, Katalog, Leitfaden)
- server/config.js: amountCents 2000, lookup_key webklar_hosting_monatlich_2000
- ensurePrice haengt einen neuen Preis an das vorhandene Stripe-Produkt (metadata.webklar),
  statt ein zweites "WEBklar Hosting-Abo" anzulegen
- abo-bedingungen.html (Uebersicht, Preise, Leistungsbeschreibung) + PDF, Leitfaden + PDF
- Tests auf 20 € umgestellt (399/399)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:29:01 +00:00
ClaudeandClaude Fable 5.1 63d3bb11bf Admin: Reiter E-Mail-Speicher, Abo mit Produkt; Schnittstelle fürs Ticketsystem
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 14:37:42 +00:00
ClaudeandClaude Opus 5.5 662f8d8144 Wechsel: Lastschrift-Fehler nur für den echten Wechsel, Abgleich ohne Webhook, Gutschrift netto, Skript prüft Stripe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:24:02 +00:00
ClaudeandClaude Fable 5.1 bc20ce0673 Zusatzspeicher: Vorschau im flexiblen Abrechnungsmodus, Aufgaben nach Zeitpunkt, keine stillen Abbrüche
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 09:16:07 +00:00
ClaudeandClaude Opus 5.5 188db4b3c1 Wechsel Hosting → Wartung: neuer Abbuchungstag, Gutschrift, 12 neue Monate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:30:21 +00:00
ClaudeandClaude Fable 5.1 379cc26dcd Zusatzspeicher: Plus sofort anteilig, Minus zum Monatsende, Aufgaben für konsoleH
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 10:48:29 +00:00
ClaudeandClaude Fable 5.1 887e1e54a0 Abo-API: Nachweis-ID nach Appwrite-Regeln, Webhook bei dauerhaften Fehlern, Tests
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 10:19:05 +00:00
ClaudeandClaude Opus 5.5 7df0b72019 Stripe: Testrabatt-Namen, Produktbild, Hosting-Leistungen, Tests für Bestandskunden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:02:03 +00:00
ClaudeandClaude Fable 5.1 c4607e9c43 Abo-API: Bestellung je Produkt, Katalog, Speicherstand, Wechsel-Möglichkeit
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 09:43:09 +00:00
ClaudeandClaude Opus 5.5 e5cc57cefd Stripe: Preise je Produkt, Testrabatt Hosting, Kundenportal „Abo verwalten“, Mindestlaufzeit ab termStartAt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:21:25 +00:00
ClaudeandClaude Opus 5.5 d097704d49 Abo-Spiegel: Produkt und tatsächlicher Preis aus den Stripe-Positionen
Neues Appwrite-Feld subscriptions.productKey (Setup-Skript). Ohne bekannten
Abo-Preis gilt ein Abo weiter als Wartungs-Abo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:31:56 +00:00