Harte Teilnehmer-Obergrenze je Tarif statt automatischem Stufenwechsel
Statt automatisch hochzustufen, blockiert das Anlegen/Reaktivieren weiterer aktiver Mitglieder, sobald das Limit des gebuchten Tarifs erreicht ist (Neuanlage, Bearbeiten mit Statuswechsel, Aktivieren- Button). mitarbeiterverwalten.php zeigt die aktuelle Auslastung an. Neuer Regressionstest scripts/check-billing-capacity.php deckt alle drei Enforcement-Stellen ab.
This commit is contained in:
@@ -129,6 +129,34 @@ function billing_find_tenant_id_by_stripe_subscription(PDO $pdo, string $stripeS
|
|||||||
return $tenantId !== false ? (int)$tenantId : null;
|
return $tenantId !== false ? (int)$tenantId : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hard cap enforcement: whether the tenant's current plan still has room
|
||||||
|
* for $additional more active participants. There is no automatic plan
|
||||||
|
* upgrade - tenants that outgrow their plan must upgrade themselves
|
||||||
|
* (mandant-einstellungen.php) before adding/reactivating more members.
|
||||||
|
*/
|
||||||
|
function billing_has_capacity_for(PDO $pdo, int $tenantId, int $additional = 1): bool
|
||||||
|
{
|
||||||
|
$billing = billing_fetch_or_init($pdo, $tenantId);
|
||||||
|
$max = billing_plans()[$billing['plan_code']]['max_participants'] ?? null;
|
||||||
|
if ($max === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return billing_count_active_participants($pdo, $tenantId) + $additional <= $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
function billing_capacity_error_message(PDO $pdo, int $tenantId): string
|
||||||
|
{
|
||||||
|
$billing = billing_fetch_or_init($pdo, $tenantId);
|
||||||
|
$plan = billing_plans()[$billing['plan_code']] ?? null;
|
||||||
|
$label = $plan['label'] ?? $billing['plan_code'];
|
||||||
|
$max = $plan['max_participants'] ?? null;
|
||||||
|
|
||||||
|
return "Euer aktueller Tarif „{$label}\" erlaubt maximal {$max} aktive Mitglieder. "
|
||||||
|
. 'Bitte upgradet unter Mandant-Einstellungen, um mehr Mitglieder freizuschalten.';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares the tenant's currently booked plan against what their active
|
* Compares the tenant's currently booked plan against what their active
|
||||||
* participant count actually requires. Purely informational until the
|
* participant count actually requires. Purely informational until the
|
||||||
|
|||||||
+41
-3
@@ -110,9 +110,47 @@ mandant-einstellungen.php (Upgrade-/Portal-Buttons)
|
|||||||
Stripe-Kunde existiert, „Zahlungsmethode verwalten / Abo kündigen"
|
Stripe-Kunde existiert, „Zahlungsmethode verwalten / Abo kündigen"
|
||||||
(führt zum Customer Portal).
|
(führt zum Customer Portal).
|
||||||
|
|
||||||
**Noch nicht umgesetzt:** automatischer Stufenwechsel bei wachsender
|
**Bewusst nicht umgesetzt:** automatischer Stufenwechsel bei wachsender
|
||||||
Teilnehmerzahl (aktuell muss der Kunde selbst erneut auf „Upgraden"
|
Teilnehmerzahl. Stattdessen gilt eine harte Obergrenze (siehe unten):
|
||||||
klicken, wenn `billing_check_tenant()` einen höheren Bedarf anzeigt).
|
wer mehr aktive Mitglieder braucht, muss selbst upgraden.
|
||||||
|
|
||||||
|
## Kapazitätsgrenze je Tarif (umgesetzt)
|
||||||
|
|
||||||
|
Statt automatisch hochzustufen, wird die Teilnehmerzahl pro Mandant hart
|
||||||
|
auf das Limit des aktuell gebuchten Tarifs begrenzt.
|
||||||
|
|
||||||
|
Umgesetzte Dateien:
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/billing.php (billing_has_capacity_for(), billing_capacity_error_message())
|
||||||
|
mitarbeiterverwalten.php (Pruefung vor Anlegen/Aktivieren/Bearbeiten)
|
||||||
|
scripts/check-billing-capacity.php (Regressionstest)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `billing_has_capacity_for($pdo, $tenantId, $additional = 1)`: prüft,
|
||||||
|
ob die aktive Teilnehmerzahl zzgl. `$additional` noch innerhalb des
|
||||||
|
Limits des gebuchten Tarifs liegt (`enterprise` = unbegrenzt).
|
||||||
|
- Geprüft an allen drei Stellen, die einen Teilnehmer aktiv anlegen oder
|
||||||
|
reaktivieren können: Neuanlage mit gesetztem „Aktiv"-Haken, Bearbeiten
|
||||||
|
eines bisher inaktiven Mitglieds mit gesetztem Haken, sowie der
|
||||||
|
dedizierte „Aktivieren"-Button. Deaktivieren, reines Bearbeiten ohne
|
||||||
|
Statuswechsel und CSV-Import (bucht nur Zahlungen auf bestehende
|
||||||
|
Mitglieder, legt keine neuen an) sind nicht betroffen.
|
||||||
|
- Bei Überschreitung erscheint eine Fehlermeldung mit Tarifname, Limit
|
||||||
|
und Verweis auf Mandant-Einstellungen zum Upgraden; die Aktion wird
|
||||||
|
nicht ausgeführt.
|
||||||
|
- `mitarbeiterverwalten.php` zeigt zusätzlich durchgehend „Aktive
|
||||||
|
Mitglieder: X von maximal Y" an, damit das Limit vor dem Anlegen
|
||||||
|
sichtbar ist.
|
||||||
|
- Live getestet (`scripts/check-billing-capacity.php`, 10 Assertionen):
|
||||||
|
10. aktives Mitglied bei Cap 10 wird angelegt, 11. wird abgelehnt und
|
||||||
|
landet nicht in der DB, inaktives Anlegen bei vollem Cap funktioniert
|
||||||
|
weiterhin, Aktivieren eines inaktiven Mitglieds bei vollem Cap wird
|
||||||
|
abgelehnt, nach Deaktivieren eines anderen Mitglieds klappt es, und
|
||||||
|
die Reaktivierung über das Bearbeiten-Formular wird ebenso geprüft.
|
||||||
|
Alle bestehenden Tenants lagen zum Zeitpunkt der Umsetzung deutlich
|
||||||
|
unter ihrem Free-Limit (max. 8 von 10 aktiven Teilnehmern), daher keine
|
||||||
|
Auswirkung auf laufenden Betrieb.
|
||||||
|
|
||||||
### Live getestet (Testmodus, kein echtes Geld)
|
### Live getestet (Testmodus, kein echtes Geld)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ include "functions.php";
|
|||||||
require_once __DIR__ . "/app/ledger.php";
|
require_once __DIR__ . "/app/ledger.php";
|
||||||
require_once __DIR__ . "/app/saas-mail.php";
|
require_once __DIR__ . "/app/saas-mail.php";
|
||||||
require_once __DIR__ . "/app/audit.php";
|
require_once __DIR__ . "/app/audit.php";
|
||||||
|
require_once __DIR__ . "/app/billing.php";
|
||||||
app_require_csrf();
|
app_require_csrf();
|
||||||
include "header.php";
|
include "header.php";
|
||||||
include "headerline.php";
|
include "headerline.php";
|
||||||
@@ -59,6 +60,8 @@ if($hasAccess){
|
|||||||
|
|
||||||
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
|
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
|
||||||
|
} elseif ($aktiv && !billing_has_capacity_for($pdo, $tenantId)) {
|
||||||
|
$fehler = billing_capacity_error_message($pdo, $tenantId);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$neueId = ledger_create_participant($pdo, $tenantId, $name, $email, $paypalname, $aktiv);
|
$neueId = ledger_create_participant($pdo, $tenantId, $name, $email, $paypalname, $aktiv);
|
||||||
@@ -75,8 +78,18 @@ if($hasAccess){
|
|||||||
$paypalname = trim((string)($_POST["paypalname"] ?? ''));
|
$paypalname = trim((string)($_POST["paypalname"] ?? ''));
|
||||||
$aktiv = isset($_POST["aktiv"]);
|
$aktiv = isset($_POST["aktiv"]);
|
||||||
|
|
||||||
|
$warAktiv = false;
|
||||||
|
foreach (ledger_fetch_participants_for_admin($pdo, $tenantId) as $m) {
|
||||||
|
if ($m['participant_id'] === $participantId) {
|
||||||
|
$warAktiv = (bool)$m['active'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
|
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
|
||||||
|
} elseif ($aktiv && !$warAktiv && !billing_has_capacity_for($pdo, $tenantId)) {
|
||||||
|
$fehler = billing_capacity_error_message($pdo, $tenantId);
|
||||||
} elseif (ledger_update_participant($pdo, $tenantId, $participantId, $name, $email, $paypalname, $aktiv)) {
|
} elseif (ledger_update_participant($pdo, $tenantId, $participantId, $name, $email, $paypalname, $aktiv)) {
|
||||||
app_audit_log($pdo, $tenantId, $actorUserId, 'participant.updated', 'participant', $participantId, ['name' => $name, 'email' => $email]);
|
app_audit_log($pdo, $tenantId, $actorUserId, 'participant.updated', 'participant', $participantId, ['name' => $name, 'email' => $email]);
|
||||||
$meldung = 'Mitglied wurde gespeichert.';
|
$meldung = 'Mitglied wurde gespeichert.';
|
||||||
@@ -85,7 +98,9 @@ if($hasAccess){
|
|||||||
}
|
}
|
||||||
} elseif ($aktion === 'aktivieren' || $aktion === 'deaktivieren') {
|
} elseif ($aktion === 'aktivieren' || $aktion === 'deaktivieren') {
|
||||||
$participantId = (int)$_POST["mitgliedID"];
|
$participantId = (int)$_POST["mitgliedID"];
|
||||||
if (ledger_set_participant_active($pdo, $tenantId, $participantId, $aktion === 'aktivieren')) {
|
if ($aktion === 'aktivieren' && !billing_has_capacity_for($pdo, $tenantId)) {
|
||||||
|
$fehler = billing_capacity_error_message($pdo, $tenantId);
|
||||||
|
} elseif (ledger_set_participant_active($pdo, $tenantId, $participantId, $aktion === 'aktivieren')) {
|
||||||
app_audit_log($pdo, $tenantId, $actorUserId, $aktion === 'aktivieren' ? 'participant.activated' : 'participant.deactivated', 'participant', $participantId);
|
app_audit_log($pdo, $tenantId, $actorUserId, $aktion === 'aktivieren' ? 'participant.activated' : 'participant.deactivated', 'participant', $participantId);
|
||||||
$meldung = $aktion === 'aktivieren' ? 'Mitglied wurde aktiviert.' : 'Mitglied wurde deaktiviert.';
|
$meldung = $aktion === 'aktivieren' ? 'Mitglied wurde aktiviert.' : 'Mitglied wurde deaktiviert.';
|
||||||
} else {
|
} else {
|
||||||
@@ -144,6 +159,9 @@ if($hasAccess){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mitglieder = ledger_fetch_participants_for_admin($pdo, $tenantId);
|
$mitglieder = ledger_fetch_participants_for_admin($pdo, $tenantId);
|
||||||
|
$billingInfo = billing_fetch_or_init($pdo, $tenantId);
|
||||||
|
$billingPlan = billing_plans()[$billingInfo['plan_code']] ?? null;
|
||||||
|
$aktiveMitgliederAnzahl = billing_count_active_participants($pdo, $tenantId);
|
||||||
$rollenLabels = ['owner' => 'Inhaber', 'admin' => 'Administrator', 'treasurer' => 'Kassenwart', 'member' => 'Mitglied', 'viewer' => 'Betrachter'];
|
$rollenLabels = ['owner' => 'Inhaber', 'admin' => 'Administrator', 'treasurer' => 'Kassenwart', 'member' => 'Mitglied', 'viewer' => 'Betrachter'];
|
||||||
$bearbeitenMitglied = null;
|
$bearbeitenMitglied = null;
|
||||||
if ($bearbeitenId !== null) {
|
if ($bearbeitenId !== null) {
|
||||||
@@ -177,6 +195,11 @@ if($hasAccess){
|
|||||||
<div class="hint-box"><p>Dev-Link zur Einladung: <a href="<?php echo saas_html($einladungslink); ?>"><?php echo saas_html($einladungslink); ?></a></p></div>
|
<div class="hint-box"><p>Dev-Link zur Einladung: <a href="<?php echo saas_html($einladungslink); ?>"><?php echo saas_html($einladungslink); ?></a></p></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p>Aktive Mitglieder: <?php echo (int)$aktiveMitgliederAnzahl; ?><?php echo $billingPlan['max_participants'] !== null ? ' von maximal ' . (int)$billingPlan['max_participants'] . ' im Tarif „' . saas_html($billingPlan['label']) . '"' : ' (Tarif „' . saas_html($billingPlan['label'] ?? $billingInfo['plan_code']) . '", unbegrenzt)'; ?>.
|
||||||
|
<?php if ($billingPlan['max_participants'] !== null && $aktiveMitgliederAnzahl >= $billingPlan['max_participants']): ?>
|
||||||
|
Limit erreicht – für weitere aktive Mitglieder ist ein <a href="mandant-einstellungen.php">Upgrade</a> nötig.
|
||||||
|
<?php endif; ?></p>
|
||||||
|
|
||||||
<?php if ($bearbeitenMitglied !== null): ?>
|
<?php if ($bearbeitenMitglied !== null): ?>
|
||||||
<h3>Bearbeiten von <?php echo saas_html($bearbeitenMitglied['display_name']); ?></h3>
|
<h3>Bearbeiten von <?php echo saas_html($bearbeitenMitglied['display_name']); ?></h3>
|
||||||
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
|
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require __DIR__ . '/dev-db.php';
|
||||||
|
|
||||||
|
$baseUrl = rtrim((string)(getenv('SMOKE_BASE_URL') ?: 'http://127.0.0.1:8080'), '/');
|
||||||
|
$pdo = dev_pdo();
|
||||||
|
$suffix = bin2hex(random_bytes(4));
|
||||||
|
$password = 'BillingCapTest123!';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string,string> $cookies
|
||||||
|
* @return array{status:int, body:string}
|
||||||
|
*/
|
||||||
|
function bc_request(string $url, array &$cookies, string $method = 'GET', ?string $postBody = null): array
|
||||||
|
{
|
||||||
|
$cookieHeader = '';
|
||||||
|
if ($cookies !== []) {
|
||||||
|
$pairs = [];
|
||||||
|
foreach ($cookies as $name => $value) {
|
||||||
|
$pairs[] = "{$name}={$value}";
|
||||||
|
}
|
||||||
|
$cookieHeader = 'Cookie: ' . implode('; ', $pairs) . "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = "User-Agent: KaffeelisteBillingCap/1.0\r\n" . $cookieHeader;
|
||||||
|
if ($postBody !== null) {
|
||||||
|
$headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$context = stream_context_create([
|
||||||
|
'http' => [
|
||||||
|
'method' => $method,
|
||||||
|
'header' => $headers,
|
||||||
|
'content' => $postBody,
|
||||||
|
'timeout' => 15,
|
||||||
|
'ignore_errors' => true,
|
||||||
|
'follow_location' => 0,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$body = @file_get_contents($url, false, $context);
|
||||||
|
$responseHeaders = $http_response_header ?? [];
|
||||||
|
$status = 0;
|
||||||
|
|
||||||
|
foreach ($responseHeaders as $header) {
|
||||||
|
if (preg_match('~^HTTP/\S+\s+(\d{3})~', $header, $m) === 1) {
|
||||||
|
$status = (int)$m[1];
|
||||||
|
}
|
||||||
|
if (preg_match('/^Set-Cookie:\s*([^=;]+)=([^;]+)/i', $header, $m) === 1) {
|
||||||
|
$cookies[$m[1]] = $m[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['status' => $status, 'body' => (string)$body];
|
||||||
|
}
|
||||||
|
|
||||||
|
function bc_csrf(string $body): string
|
||||||
|
{
|
||||||
|
preg_match('/name="csrf_token" value="([^"]+)"/', $body, $matches);
|
||||||
|
return $matches[1] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tenantId = null;
|
||||||
|
$userId = null;
|
||||||
|
$extraParticipantIds = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO tenants (slug, name, status) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute(["billingcap-{$suffix}", 'Billing Cap Tenant', 'active']);
|
||||||
|
$tenantId = (int)$pdo->lastInsertId();
|
||||||
|
$pdo->prepare('INSERT INTO tenant_settings (tenant_id) VALUES (?)')->execute([$tenantId]);
|
||||||
|
|
||||||
|
$email = "billingcap-owner-{$suffix}@test.local";
|
||||||
|
$pdo->prepare('INSERT INTO users (email, email_norm, display_name, password_hash, status) VALUES (?, ?, ?, ?, ?)')
|
||||||
|
->execute([$email, $email, 'Billing Cap Owner', password_hash($password, PASSWORD_DEFAULT), 'active']);
|
||||||
|
$userId = (int)$pdo->lastInsertId();
|
||||||
|
|
||||||
|
$pdo->prepare('INSERT INTO tenant_memberships (tenant_id, user_id, role, status, joined_at) VALUES (?, ?, ?, ?, NOW())')
|
||||||
|
->execute([$tenantId, $userId, 'owner', 'active']);
|
||||||
|
$pdo->prepare('INSERT INTO participants (tenant_id, user_id, display_name, email, email_norm, active) VALUES (?, ?, ?, ?, ?, 1)')
|
||||||
|
->execute([$tenantId, $userId, 'Billing Cap Owner', $email, $email]);
|
||||||
|
|
||||||
|
// Free-Plan-Limit ist 10. Owner zaehlt schon als 1 aktiver Teilnehmer,
|
||||||
|
// also 8 weitere anlegen, um bei 9 von 10 zu landen.
|
||||||
|
for ($i = 1; $i <= 8; $i++) {
|
||||||
|
$pEmail = "billingcap-seed{$i}-{$suffix}@test.local";
|
||||||
|
$pdo->prepare('INSERT INTO participants (tenant_id, display_name, email, email_norm, active) VALUES (?, ?, ?, ?, 1)')
|
||||||
|
->execute([$tenantId, "Seed {$i}", $pEmail, $pEmail]);
|
||||||
|
$extraParticipantIds[] = (int)$pdo->lastInsertId();
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
fwrite(STDERR, "Billing capacity test setup failed: {$e->getMessage()}\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$failures = [];
|
||||||
|
$passes = 0;
|
||||||
|
|
||||||
|
function bc_assert(string $label, bool $condition, array &$failures, int &$passes): void
|
||||||
|
{
|
||||||
|
if ($condition) {
|
||||||
|
$passes++;
|
||||||
|
echo "PASS {$label}\n";
|
||||||
|
} else {
|
||||||
|
$failures[] = $label;
|
||||||
|
echo "FAIL {$label}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cookies = [];
|
||||||
|
$loginPage = bc_request("{$baseUrl}/login.php", $cookies);
|
||||||
|
$csrf = bc_csrf($loginPage['body']);
|
||||||
|
bc_request("{$baseUrl}/login.php", $cookies, 'POST', http_build_query([
|
||||||
|
'csrf_token' => $csrf,
|
||||||
|
'email' => $email,
|
||||||
|
'password' => $password,
|
||||||
|
'tenant_slug' => "billingcap-{$suffix}",
|
||||||
|
]));
|
||||||
|
|
||||||
|
// 9 aktive Teilnehmer vorhanden (Cap 10) -> ein aktives Anlegen muss klappen (10/10).
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$body10 = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'anlegen',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'name' => 'Zehntes Mitglied',
|
||||||
|
'email' => "billingcap-tenth-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
'aktiv' => '1',
|
||||||
|
]));
|
||||||
|
bc_assert('10. aktives Mitglied bei Cap 10 wird angelegt', str_contains($body10['body'], 'wurde angelegt'), $failures, $passes);
|
||||||
|
|
||||||
|
// Jetzt bei 10/10 -> ein weiteres aktives Anlegen muss abgelehnt werden.
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$body11 = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'anlegen',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'name' => 'Elftes Mitglied',
|
||||||
|
'email' => "billingcap-eleventh-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
'aktiv' => '1',
|
||||||
|
]));
|
||||||
|
bc_assert('11. aktives Mitglied bei Cap 10 wird abgelehnt', str_contains($body11['body'], 'maximal 10 aktive Mitglieder'), $failures, $passes);
|
||||||
|
bc_assert('11. Mitglied wurde nicht in der DB angelegt', (int)$pdo->query("SELECT COUNT(*) FROM participants WHERE tenant_id = {$tenantId} AND email = 'billingcap-eleventh-{$suffix}@test.local'")->fetchColumn() === 0, $failures, $passes);
|
||||||
|
|
||||||
|
// Inaktives Anlegen bei vollem Cap muss weiterhin moeglich sein.
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$bodyInactive = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'anlegen',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'name' => 'Inaktives Mitglied',
|
||||||
|
'email' => "billingcap-inactive-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
]));
|
||||||
|
bc_assert('Inaktives Mitglied bei vollem Cap wird angelegt', str_contains($bodyInactive['body'], 'wurde angelegt'), $failures, $passes);
|
||||||
|
|
||||||
|
$inactiveId = (int)$pdo->query("SELECT id FROM participants WHERE tenant_id = {$tenantId} AND email = 'billingcap-inactive-{$suffix}@test.local'")->fetchColumn();
|
||||||
|
bc_assert('Neu angelegtes Mitglied ist tatsaechlich inaktiv', (int)$pdo->query("SELECT active FROM participants WHERE id = {$inactiveId}")->fetchColumn() === 0, $failures, $passes);
|
||||||
|
|
||||||
|
// Aktivieren des inaktiven Mitglieds bei vollem Cap muss abgelehnt werden.
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$bodyActivateBlocked = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'aktivieren',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'mitgliedID' => (string)$inactiveId,
|
||||||
|
]));
|
||||||
|
bc_assert('Aktivieren bei vollem Cap wird abgelehnt', str_contains($bodyActivateBlocked['body'], 'maximal 10 aktive Mitglieder'), $failures, $passes);
|
||||||
|
bc_assert('Mitglied ist nach abgelehntem Aktivieren weiterhin inaktiv', (int)$pdo->query("SELECT active FROM participants WHERE id = {$inactiveId}")->fetchColumn() === 0, $failures, $passes);
|
||||||
|
|
||||||
|
// Ein aktives Mitglied deaktivieren (Platz schaffen), dann klappt Aktivieren.
|
||||||
|
$freeUpId = $extraParticipantIds[0];
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'deaktivieren',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'mitgliedID' => (string)$freeUpId,
|
||||||
|
]));
|
||||||
|
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$bodyActivateOk = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'aktivieren',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'mitgliedID' => (string)$inactiveId,
|
||||||
|
]));
|
||||||
|
bc_assert('Aktivieren nach Platzschaffen (Deaktivierung) klappt', str_contains($bodyActivateOk['body'], 'wurde aktiviert'), $failures, $passes);
|
||||||
|
|
||||||
|
// bearbeitenspeichern: ein weiteres inaktives Mitglied anlegen, dann per
|
||||||
|
// Bearbeiten-Formular aktivieren versuchen, wieder bei vollem Cap (10/10).
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'anlegen',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'name' => 'Editier-Testmitglied',
|
||||||
|
'email' => "billingcap-edit-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
]));
|
||||||
|
$editId = (int)$pdo->query("SELECT id FROM participants WHERE tenant_id = {$tenantId} AND email = 'billingcap-edit-{$suffix}@test.local'")->fetchColumn();
|
||||||
|
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$bodyEditBlocked = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'bearbeitenspeichern',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'mitgliedID' => (string)$editId,
|
||||||
|
'name' => 'Editier-Testmitglied',
|
||||||
|
'email' => "billingcap-edit-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
'aktiv' => '1',
|
||||||
|
]));
|
||||||
|
bc_assert('bearbeitenspeichern-Reaktivierung bei vollem Cap wird abgelehnt', str_contains($bodyEditBlocked['body'], 'maximal 10 aktive Mitglieder'), $failures, $passes);
|
||||||
|
|
||||||
|
// Editieren ohne Statuswechsel (bleibt inaktiv) muss weiterhin klappen.
|
||||||
|
$page = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies);
|
||||||
|
$bodyEditOk = bc_request("{$baseUrl}/mitarbeiterverwalten.php", $cookies, 'POST', http_build_query([
|
||||||
|
'aktion' => 'bearbeitenspeichern',
|
||||||
|
'csrf_token' => bc_csrf($page['body']),
|
||||||
|
'mitgliedID' => (string)$editId,
|
||||||
|
'name' => 'Editier-Testmitglied Neu',
|
||||||
|
'email' => "billingcap-edit-{$suffix}@test.local",
|
||||||
|
'paypalname' => '',
|
||||||
|
]));
|
||||||
|
bc_assert('bearbeitenspeichern ohne Aktivierung bei vollem Cap klappt', str_contains($bodyEditOk['body'], 'wurde gespeichert'), $failures, $passes);
|
||||||
|
|
||||||
|
// Aufraeumen
|
||||||
|
$pdo->prepare('DELETE FROM participants WHERE tenant_id = ?')->execute([$tenantId]);
|
||||||
|
$pdo->prepare('DELETE FROM tenant_memberships WHERE tenant_id = ?')->execute([$tenantId]);
|
||||||
|
$pdo->prepare('DELETE FROM tenant_billing WHERE tenant_id = ?')->execute([$tenantId]);
|
||||||
|
$pdo->prepare('DELETE FROM user_auth_tokens WHERE user_id = ?')->execute([$userId]);
|
||||||
|
$pdo->prepare('DELETE FROM users WHERE id = ?')->execute([$userId]);
|
||||||
|
$pdo->prepare('DELETE FROM tenant_settings WHERE tenant_id = ?')->execute([$tenantId]);
|
||||||
|
$pdo->prepare('DELETE FROM tenants WHERE id = ?')->execute([$tenantId]);
|
||||||
|
|
||||||
|
if ($failures !== []) {
|
||||||
|
echo "\nBilling capacity check failed with " . count($failures) . " failure(s):\n";
|
||||||
|
foreach ($failures as $failure) {
|
||||||
|
echo "- {$failure}\n";
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\nBilling capacity check passed with {$passes} assertions.\n";
|
||||||
Reference in New Issue
Block a user