Billing Phase 1: Tarifstatus-Datenmodell und Tariflogik je Mandant

Vorbereitung fuer Stripe Billing (Zahlungseinzug) + bestehendes Dolibarr
des Kunden (Rechnungsstellung/Buchhaltung) statt eines eigenen Rechnungs-
systems oder eines zusaetzlichen ERP nur fuer Kaffeeliste - Begruendung
in docs/billing.md.

- Neue Tabelle tenant_billing (plan_code, subscription_status, sowie
  bereits vorbereitete, noch ungenutzte Felder fuer Stripe/Dolibarr-IDs).
- app/billing.php: billing_plans() als einzige Quelle der Tarifstufen
  (synchron mit preise.php), billing_required_plan_code() anhand aktiver
  Teilnehmerzahl, billing_check_tenant() vergleicht gebuchten mit
  benoetigtem Tarif - rein informativ, kein Enforcement, solange Stripe
  nicht angebunden ist.
- Neue Mandanten starten automatisch auf plan_code='free' bei der
  Registrierung.
- mandant-einstellungen.php zeigt Owner/Admin ihren aktuellen Tarif und
  Teilnehmerstand, mit Hinweis bei Bedarf einer hoeheren Stufe.
- Back-Office zeigt zusaetzlich zur Mandantenliste den gebuchten und
  (falls abweichend) den tatsaechlich benoetigten Tarif.

Live getestet: Tarifgrenzen (10/25/50/150) mit einem Mandanten unter und
einem ueber dem Freikontingent geprueft, UI in Mandant-Einstellungen und
Back-Office verifiziert. Alle M8-Isolations-/Rollenmatrix-Tests weiterhin
gruen.

Phase 2 (Stripe) und Phase 3 (Dolibarr-Sync) folgen, sobald Test-Keys
beziehungsweise Sandbox-Zugang vorliegen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 23:38:28 +02:00
co-authored by Claude Sonnet 5
parent d08621b616
commit ade1fd0ed9
7 changed files with 249 additions and 2 deletions
+2
View File
@@ -5,6 +5,7 @@ declare(strict_types=1);
require_once __DIR__ . '/bootstrap.php';
require_once __DIR__ . '/database.php';
require_once __DIR__ . '/faq.php';
require_once __DIR__ . '/billing.php';
function saas_email_norm(string $email): string
{
@@ -1061,6 +1062,7 @@ function saas_register_tenant_owner(PDO $pdo, array $input): array
$stmt->execute([$tenantId, 20, 1, 0, '']);
faq_seed_default_entries($pdo, $tenantId);
billing_fetch_or_init($pdo, $tenantId);
if ($existingUser === false) {
$stmt = $pdo->prepare(