M3 Mandant-Einstellungen und Rollencheck ergänzen
- zentrale Owner/Admin-Rollenpruefung in SaaS-Auth ergaenzen - Mandant-Einstellungen fuer Preise, PayPal und Basisdaten bauen - Settings-Flow-Test, Navigation und M3-Dokumentation aktualisieren
This commit is contained in:
@@ -24,6 +24,40 @@ function saas_html(mixed $value): string
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function saas_parse_money_cents(mixed $value): ?int
|
||||
{
|
||||
$normalized = trim((string)$value);
|
||||
$normalized = str_replace(["\xc2\xa0", ' '], '', $normalized);
|
||||
|
||||
if ($normalized === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_contains($normalized, ',') && str_contains($normalized, '.')) {
|
||||
$normalized = str_replace('.', '', $normalized);
|
||||
}
|
||||
|
||||
$normalized = str_replace(',', '.', $normalized);
|
||||
if (!preg_match('/^-?[0-9]+(?:\.[0-9]{1,2})?$/', $normalized)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$negative = str_starts_with($normalized, '-');
|
||||
if ($negative) {
|
||||
$normalized = substr($normalized, 1);
|
||||
}
|
||||
|
||||
[$euros, $cents] = array_pad(explode('.', $normalized, 2), 2, '0');
|
||||
$result = ((int)$euros * 100) + (int)str_pad(substr($cents, 0, 2), 2, '0');
|
||||
|
||||
return $negative ? -$result : $result;
|
||||
}
|
||||
|
||||
function saas_format_money_cents(mixed $cents): string
|
||||
{
|
||||
return number_format(((int)$cents) / 100, 2, ',', '');
|
||||
}
|
||||
|
||||
function saas_current_user(?PDO $pdo = null): ?array
|
||||
{
|
||||
static $cached = false;
|
||||
@@ -111,6 +145,182 @@ function saas_require_login(): array
|
||||
exit;
|
||||
}
|
||||
|
||||
function saas_user_has_role(array|string $roles, ?array $user = null): bool
|
||||
{
|
||||
$user = $user ?? saas_current_user();
|
||||
if ($user === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$allowed = is_array($roles) ? $roles : [$roles];
|
||||
|
||||
return in_array((string)($user['role'] ?? ''), $allowed, true);
|
||||
}
|
||||
|
||||
function saas_require_role(array|string $roles): array
|
||||
{
|
||||
$user = saas_require_login();
|
||||
if (saas_user_has_role($roles, $user)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
http_response_code(403);
|
||||
die('Keine Berechtigung.');
|
||||
}
|
||||
|
||||
function saas_can_manage_tenant_settings(?array $user = null): bool
|
||||
{
|
||||
return saas_user_has_role(['owner', 'admin'], $user);
|
||||
}
|
||||
|
||||
function saas_fetch_tenant_settings(PDO $pdo, int $tenantId): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT
|
||||
t.id AS tenant_id,
|
||||
t.slug,
|
||||
t.name,
|
||||
t.status,
|
||||
t.timezone,
|
||||
t.locale,
|
||||
t.currency_code,
|
||||
ts.mark_price_cents,
|
||||
ts.self_entry_enabled,
|
||||
ts.paypal_enabled,
|
||||
ts.paypal_url_template,
|
||||
ts.sheet_window_days,
|
||||
ts.negative_warning_cents
|
||||
FROM tenants t
|
||||
LEFT JOIN tenant_settings ts ON ts.tenant_id = t.id
|
||||
WHERE t.id = ?
|
||||
LIMIT 1'
|
||||
);
|
||||
$stmt->execute([$tenantId]);
|
||||
$settings = $stmt->fetch();
|
||||
|
||||
if ($settings === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($settings['mark_price_cents'] === null) {
|
||||
$pdo->prepare('INSERT INTO tenant_settings (tenant_id) VALUES (?)')->execute([$tenantId]);
|
||||
|
||||
return saas_fetch_tenant_settings($pdo, $tenantId);
|
||||
}
|
||||
|
||||
return [
|
||||
'tenant_id' => (int)$settings['tenant_id'],
|
||||
'slug' => (string)$settings['slug'],
|
||||
'name' => (string)$settings['name'],
|
||||
'status' => (string)$settings['status'],
|
||||
'timezone' => (string)$settings['timezone'],
|
||||
'locale' => (string)$settings['locale'],
|
||||
'currency_code' => (string)$settings['currency_code'],
|
||||
'mark_price_cents' => (int)$settings['mark_price_cents'],
|
||||
'self_entry_enabled' => (int)$settings['self_entry_enabled'],
|
||||
'paypal_enabled' => (int)$settings['paypal_enabled'],
|
||||
'paypal_url_template' => (string)$settings['paypal_url_template'],
|
||||
'sheet_window_days' => (int)$settings['sheet_window_days'],
|
||||
'negative_warning_cents' => (int)$settings['negative_warning_cents'],
|
||||
];
|
||||
}
|
||||
|
||||
function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): array
|
||||
{
|
||||
$tenantName = trim((string)($input['tenant_name'] ?? ''));
|
||||
$timezone = trim((string)($input['timezone'] ?? 'Europe/Berlin'));
|
||||
$locale = trim((string)($input['locale'] ?? 'de-DE'));
|
||||
$currencyCode = strtoupper(trim((string)($input['currency_code'] ?? 'EUR')));
|
||||
$markPriceCents = saas_parse_money_cents($input['mark_price'] ?? '');
|
||||
$sheetWindowDays = filter_var($input['sheet_window_days'] ?? null, FILTER_VALIDATE_INT);
|
||||
$negativeWarningCents = saas_parse_money_cents($input['negative_warning'] ?? '');
|
||||
$paypalUrlTemplate = trim((string)($input['paypal_url_template'] ?? ''));
|
||||
|
||||
$errors = [];
|
||||
if (strlen($tenantName) < 3 || strlen($tenantName) > 255) {
|
||||
$errors[] = 'Der Kundenname muss zwischen 3 und 255 Zeichen lang sein.';
|
||||
}
|
||||
if (!in_array($timezone, DateTimeZone::listIdentifiers(), true)) {
|
||||
$errors[] = 'Die Zeitzone ist ungueltig.';
|
||||
}
|
||||
if (!preg_match('/^[a-z]{2}-[A-Z]{2}$/', $locale)) {
|
||||
$errors[] = 'Die Locale muss dem Muster de-DE entsprechen.';
|
||||
}
|
||||
if (!preg_match('/^[A-Z]{3}$/', $currencyCode)) {
|
||||
$errors[] = 'Der Waehrungscode muss aus drei Grossbuchstaben bestehen.';
|
||||
}
|
||||
if ($markPriceCents === null || $markPriceCents < 1 || $markPriceCents > 10000) {
|
||||
$errors[] = 'Der Preis pro Strich muss zwischen 0,01 und 100,00 liegen.';
|
||||
}
|
||||
if ($sheetWindowDays === false || $sheetWindowDays < 1 || $sheetWindowDays > 365) {
|
||||
$errors[] = 'Das Listenfenster muss zwischen 1 und 365 Tagen liegen.';
|
||||
}
|
||||
if ($negativeWarningCents === null || $negativeWarningCents < 0 || $negativeWarningCents > 100000) {
|
||||
$errors[] = 'Die Schulden-Warnschwelle muss zwischen 0,00 und 1000,00 liegen.';
|
||||
}
|
||||
if (strlen($paypalUrlTemplate) > 1000) {
|
||||
$errors[] = 'Der PayPal-Link ist zu lang.';
|
||||
}
|
||||
|
||||
if ($errors !== []) {
|
||||
return ['ok' => false, 'errors' => $errors];
|
||||
}
|
||||
|
||||
$selfEntryEnabled = !empty($input['self_entry_enabled']) ? 1 : 0;
|
||||
$paypalEnabled = !empty($input['paypal_enabled']) ? 1 : 0;
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'UPDATE tenants
|
||||
SET name = ?, timezone = ?, locale = ?, currency_code = ?
|
||||
WHERE id = ?'
|
||||
);
|
||||
$stmt->execute([$tenantName, $timezone, $locale, $currencyCode, $tenantId]);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO tenant_settings
|
||||
(tenant_id, mark_price_cents, self_entry_enabled, paypal_enabled,
|
||||
paypal_url_template, sheet_window_days, negative_warning_cents)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
mark_price_cents = VALUES(mark_price_cents),
|
||||
self_entry_enabled = VALUES(self_entry_enabled),
|
||||
paypal_enabled = VALUES(paypal_enabled),
|
||||
paypal_url_template = VALUES(paypal_url_template),
|
||||
sheet_window_days = VALUES(sheet_window_days),
|
||||
negative_warning_cents = VALUES(negative_warning_cents)'
|
||||
);
|
||||
$stmt->execute([
|
||||
$tenantId,
|
||||
$markPriceCents,
|
||||
$selfEntryEnabled,
|
||||
$paypalEnabled,
|
||||
$paypalUrlTemplate,
|
||||
(int)$sheetWindowDays,
|
||||
-abs($negativeWarningCents),
|
||||
]);
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => false,
|
||||
'errors' => ['Die Einstellungen konnten nicht gespeichert werden.'],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'errors' => [],
|
||||
'settings' => saas_fetch_tenant_settings($pdo, $tenantId),
|
||||
];
|
||||
}
|
||||
|
||||
function saas_authenticate(PDO $pdo, string $email, string $password, string $tenantSlug = ''): array
|
||||
{
|
||||
$emailNorm = saas_email_norm($email);
|
||||
|
||||
@@ -140,8 +140,8 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
|
||||
eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`.
|
||||
- CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen.
|
||||
- Golden Master weiterhin gruen mit 104 Assertions.
|
||||
- HTTP-Smoke weiterhin gruen mit 16 sicheren Seiten inklusive Login und
|
||||
Registrierung.
|
||||
- HTTP-Smoke weiterhin gruen mit 17 sicheren Seiten inklusive Login,
|
||||
Registrierung und geschuetzter Mandant-Einstellungen.
|
||||
|
||||
## Bewusste Grenzen
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ Ergaenzende Skripte:
|
||||
scripts/backfill-default-tenant.php
|
||||
scripts/check-m3-saas-basis.php
|
||||
scripts/check-m3-auth-flow.php
|
||||
scripts/check-m3-settings-flow.php
|
||||
```
|
||||
|
||||
Wichtige Regeln:
|
||||
@@ -136,8 +137,27 @@ Noch offen im M3-Auth-Scope:
|
||||
- Passwort-Reset.
|
||||
- E-Mail-Verifikation.
|
||||
- Tenant-Aufloesung ueber Subdomain oder Custom Domain.
|
||||
- Zentrale Rollenpruefung fuer neue SaaS-Seiten.
|
||||
- Owner-/Admin-Seite fuer Tenant-Grundeinstellungen.
|
||||
- Weitergehende Rollenmatrix fuer spaetere SaaS-Seiten.
|
||||
|
||||
## Rollen und Grundeinstellungen
|
||||
|
||||
Umgesetzte Dateien:
|
||||
|
||||
```text
|
||||
mandant-einstellungen.php
|
||||
```
|
||||
|
||||
Umgesetzter Umfang:
|
||||
|
||||
- `saas_user_has_role()`, `saas_require_role()` und
|
||||
`saas_can_manage_tenant_settings()` zentralisieren die erste Rollenpruefung.
|
||||
- Owner und Admin duerfen Tenant-Grundeinstellungen bearbeiten.
|
||||
- Die Seite bearbeitet Kundenname, Zeitzone, Locale, Waehrung, Preis pro
|
||||
Strich, Web-Striche, PayPal-Link, Listenfenster und Schulden-Warnschwelle.
|
||||
- Geldwerte werden in der UI als Euro-Werte erfasst und weiterhin als Cent-
|
||||
Integer gespeichert.
|
||||
- `konto.php` und die Sidebar verlinken die Einstellungen nur fuer passende
|
||||
Rollen.
|
||||
|
||||
## Erste Akzeptanzkriterien
|
||||
|
||||
@@ -150,6 +170,7 @@ Noch offen im M3-Auth-Scope:
|
||||
- Admins werden als tenant-scoped Rolle abgebildet: erfuellt.
|
||||
- Registrierung und Login funktionieren fuer einen neuen Test-Tenant:
|
||||
erfuellt.
|
||||
- Owner-/Admin-Grundeinstellungen koennen aktualisiert werden: erfuellt.
|
||||
- Golden-Master und HTTP-Smoke bleiben gruen: erfuellt.
|
||||
|
||||
## Risiken
|
||||
@@ -171,6 +192,8 @@ Noch offen im M3-Auth-Scope:
|
||||
5. Kontrollskript fuer Tenant/Participant/Role-Counts schreiben: erledigt.
|
||||
6. Login-/Registrierungsrouten bauen: erledigt.
|
||||
7. Auth-Flow-Kontrollskript schreiben: erledigt.
|
||||
8. Golden-Master und HTTP-Smoke ausfuehren: erledigt.
|
||||
9. Passwort-Reset, E-Mail-Verifikation und zentrale Rollenpruefung planen:
|
||||
8. Zentrale Rollenpruefung und Mandant-Einstellungen bauen: erledigt.
|
||||
9. Settings-Flow-Kontrollskript schreiben: erledigt.
|
||||
10. Golden-Master und HTTP-Smoke ausfuehren: erledigt.
|
||||
11. Passwort-Reset, E-Mail-Verifikation und Tenant-Aufloesung planen:
|
||||
naechster Schritt.
|
||||
|
||||
@@ -422,8 +422,8 @@ Schritte:
|
||||
- Login und Logout bauen: erster Flow erledigt.
|
||||
- Passwort-Reset und E-Mail-Verifikation bauen.
|
||||
- Tenant-Aufloesung definieren.
|
||||
- Rollenpruefung zentralisieren.
|
||||
- Erste Admin-/Owner-Seite fuer Grundeinstellungen.
|
||||
- Rollenpruefung zentralisieren: erster Owner/Admin-Check erledigt.
|
||||
- Erste Admin-/Owner-Seite fuer Grundeinstellungen: erledigt.
|
||||
|
||||
Ergebnis:
|
||||
|
||||
|
||||
@@ -38,6 +38,13 @@ $saasNavUser = function_exists('saas_current_user') ? saas_current_user() : null
|
||||
if ($saasNavUser !== null) {
|
||||
?>
|
||||
<li><a href="konto.php">Kundenkonto</a></li>
|
||||
<?php
|
||||
if (function_exists('saas_can_manage_tenant_settings') && saas_can_manage_tenant_settings($saasNavUser)) {
|
||||
?>
|
||||
<li><a href="mandant-einstellungen.php">Mandant-Einstellungen</a></li>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<li><a href="logout.php">Logout</a></li>
|
||||
<?php
|
||||
} else {
|
||||
|
||||
@@ -40,6 +40,12 @@ include 'nav.php';
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php if (saas_can_manage_tenant_settings($user)): ?>
|
||||
<ul class="actions">
|
||||
<li><a href="mandant-einstellungen.php" class="button">Mandant-Einstellungen</a></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="logout.php">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
<button type="submit">Abmelden</button>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/functions.php';
|
||||
|
||||
$pdo = app_db_pdo();
|
||||
$user = saas_require_login();
|
||||
$canManageSettings = saas_can_manage_tenant_settings($user);
|
||||
$errors = [];
|
||||
$saved = false;
|
||||
$settings = null;
|
||||
|
||||
if ($canManageSettings) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
app_require_csrf();
|
||||
$result = saas_update_tenant_settings($pdo, (int)$user['tenant_id'], $_POST);
|
||||
if ($result['ok']) {
|
||||
$saved = true;
|
||||
$settings = $result['settings'];
|
||||
} else {
|
||||
$errors = $result['errors'];
|
||||
$settings = [
|
||||
'name' => $_POST['tenant_name'] ?? '',
|
||||
'timezone' => $_POST['timezone'] ?? 'Europe/Berlin',
|
||||
'locale' => $_POST['locale'] ?? 'de-DE',
|
||||
'currency_code' => $_POST['currency_code'] ?? 'EUR',
|
||||
'mark_price_cents' => saas_parse_money_cents($_POST['mark_price'] ?? '') ?? 0,
|
||||
'self_entry_enabled' => !empty($_POST['self_entry_enabled']) ? 1 : 0,
|
||||
'paypal_enabled' => !empty($_POST['paypal_enabled']) ? 1 : 0,
|
||||
'paypal_url_template' => $_POST['paypal_url_template'] ?? '',
|
||||
'sheet_window_days' => $_POST['sheet_window_days'] ?? 100,
|
||||
'negative_warning_cents' => -abs(saas_parse_money_cents($_POST['negative_warning'] ?? '') ?? 0),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$settings = saas_fetch_tenant_settings($pdo, (int)$user['tenant_id']);
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
include 'headerline.php';
|
||||
include 'nav.php';
|
||||
?>
|
||||
|
||||
<section id="banner">
|
||||
<div class="content">
|
||||
<h2>Mandant-Einstellungen</h2>
|
||||
|
||||
<?php if (!$canManageSettings): ?>
|
||||
<?php http_response_code(403); ?>
|
||||
<div class="hint-box error"><p>Du hast keine Berechtigung fuer diese Einstellungen.</p></div>
|
||||
<?php elseif ($settings === null): ?>
|
||||
<div class="hint-box error"><p>Die Einstellungen konnten nicht geladen werden.</p></div>
|
||||
<?php else: ?>
|
||||
<?php if ($saved): ?>
|
||||
<div class="hint-box success"><p>Die Einstellungen wurden gespeichert.</p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($errors !== []): ?>
|
||||
<div class="hint-box error">
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<p><?php echo saas_html($error); ?></p>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="mandant-einstellungen.php">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
<div class="row">
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="tenant_name">Kundenname</label>
|
||||
<input type="text" name="tenant_name" id="tenant_name" value="<?php echo saas_html($settings['name']); ?>" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="timezone">Zeitzone</label>
|
||||
<input type="text" name="timezone" id="timezone" value="<?php echo saas_html($settings['timezone']); ?>" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="locale">Locale</label>
|
||||
<input type="text" name="locale" id="locale" value="<?php echo saas_html($settings['locale']); ?>" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="currency_code">Waehrung</label>
|
||||
<input type="text" name="currency_code" id="currency_code" value="<?php echo saas_html($settings['currency_code']); ?>" maxlength="3" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="mark_price">Preis pro Strich</label>
|
||||
<input type="text" name="mark_price" id="mark_price" value="<?php echo saas_html(saas_format_money_cents($settings['mark_price_cents'])); ?>" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="sheet_window_days">Listenfenster in Tagen</label>
|
||||
<input type="number" name="sheet_window_days" id="sheet_window_days" min="1" max="365" value="<?php echo saas_html($settings['sheet_window_days']); ?>" required>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<label for="negative_warning">Schulden-Warnschwelle</label>
|
||||
<input type="text" name="negative_warning" id="negative_warning" value="<?php echo saas_html(saas_format_money_cents(abs((int)$settings['negative_warning_cents']))); ?>" required>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="paypal_url_template">PayPal-Link</label>
|
||||
<input type="text" name="paypal_url_template" id="paypal_url_template" value="<?php echo saas_html($settings['paypal_url_template']); ?>">
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<input type="checkbox" id="self_entry_enabled" name="self_entry_enabled" value="1" <?php echo (int)$settings['self_entry_enabled'] === 1 ? 'checked' : ''; ?>>
|
||||
<label for="self_entry_enabled">Web-Striche erlauben</label>
|
||||
</div>
|
||||
<div class="col-6 col-12-small">
|
||||
<input type="checkbox" id="paypal_enabled" name="paypal_enabled" value="1" <?php echo (int)$settings['paypal_enabled'] === 1 ? 'checked' : ''; ?>>
|
||||
<label for="paypal_enabled">PayPal anzeigen</label>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="actions">
|
||||
<li><button type="submit">Speichern</button></li>
|
||||
<li><a href="konto.php" class="button alt">Zurueck</a></li>
|
||||
</ul>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/dev-db.php';
|
||||
require __DIR__ . '/../app/saas-auth.php';
|
||||
|
||||
function settings_check_assert(string $label, bool $condition, array &$failures, int &$passes): void
|
||||
{
|
||||
if ($condition) {
|
||||
$passes++;
|
||||
echo "PASS {$label}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$failures[] = $label;
|
||||
echo "FAIL {$label}\n";
|
||||
}
|
||||
|
||||
function settings_check_cleanup(PDO $pdo, string $slug, string $emailNorm): void
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT id FROM tenants WHERE slug = ?');
|
||||
$stmt->execute([$slug]);
|
||||
$tenantId = $stmt->fetchColumn();
|
||||
|
||||
if ($tenantId !== false) {
|
||||
$tenantId = (int)$tenantId;
|
||||
$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_settings WHERE tenant_id = ?')->execute([$tenantId]);
|
||||
$pdo->prepare('DELETE FROM tenants WHERE id = ?')->execute([$tenantId]);
|
||||
}
|
||||
|
||||
$pdo->prepare('DELETE FROM users WHERE email_norm = ?')->execute([$emailNorm]);
|
||||
}
|
||||
|
||||
$pdo = dev_pdo();
|
||||
dev_apply_schema($pdo);
|
||||
|
||||
$slug = 'm3-settings-flow-check';
|
||||
$email = 'm3-settings-flow-check@test.local';
|
||||
$emailNorm = saas_email_norm($email);
|
||||
$password = 'M3-settings-flow-123';
|
||||
$failures = [];
|
||||
$passes = 0;
|
||||
|
||||
settings_check_cleanup($pdo, $slug, $emailNorm);
|
||||
|
||||
$registration = saas_register_tenant_owner($pdo, [
|
||||
'tenant_name' => 'M3 Settings Flow Check',
|
||||
'tenant_slug' => $slug,
|
||||
'display_name' => 'M3 Settings Check',
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'password_confirm' => $password,
|
||||
]);
|
||||
|
||||
settings_check_assert('registration succeeds', $registration['ok'] === true, $failures, $passes);
|
||||
|
||||
if ($registration['ok']) {
|
||||
$identity = $registration['identity'];
|
||||
$tenantId = (int)$identity['tenant_id'];
|
||||
settings_check_assert('owner can manage settings', saas_can_manage_tenant_settings($identity), $failures, $passes);
|
||||
settings_check_assert(
|
||||
'member cannot manage settings',
|
||||
!saas_can_manage_tenant_settings(['role' => 'member']),
|
||||
$failures,
|
||||
$passes
|
||||
);
|
||||
|
||||
$settings = saas_fetch_tenant_settings($pdo, $tenantId);
|
||||
settings_check_assert('settings can be fetched', $settings !== null, $failures, $passes);
|
||||
if ($settings !== null) {
|
||||
settings_check_assert('default mark price is 20 cents', $settings['mark_price_cents'] === 20, $failures, $passes);
|
||||
}
|
||||
|
||||
$update = saas_update_tenant_settings($pdo, $tenantId, [
|
||||
'tenant_name' => 'M3 Settings Flow Updated',
|
||||
'timezone' => 'Europe/Berlin',
|
||||
'locale' => 'de-DE',
|
||||
'currency_code' => 'EUR',
|
||||
'mark_price' => '0,35',
|
||||
'sheet_window_days' => '90',
|
||||
'negative_warning' => '12,50',
|
||||
'paypal_url_template' => 'https://paypal.example/m3/',
|
||||
'self_entry_enabled' => '1',
|
||||
'paypal_enabled' => '1',
|
||||
]);
|
||||
|
||||
settings_check_assert('settings update succeeds', $update['ok'] === true, $failures, $passes);
|
||||
|
||||
$updated = saas_fetch_tenant_settings($pdo, $tenantId);
|
||||
settings_check_assert('updated settings can be fetched', $updated !== null, $failures, $passes);
|
||||
if ($updated !== null) {
|
||||
settings_check_assert('tenant name is updated', $updated['name'] === 'M3 Settings Flow Updated', $failures, $passes);
|
||||
settings_check_assert('mark price is updated', $updated['mark_price_cents'] === 35, $failures, $passes);
|
||||
settings_check_assert('sheet window is updated', $updated['sheet_window_days'] === 90, $failures, $passes);
|
||||
settings_check_assert('negative warning is stored negative', $updated['negative_warning_cents'] === -1250, $failures, $passes);
|
||||
settings_check_assert('paypal is enabled', $updated['paypal_enabled'] === 1, $failures, $passes);
|
||||
settings_check_assert('self entry is enabled', $updated['self_entry_enabled'] === 1, $failures, $passes);
|
||||
}
|
||||
}
|
||||
|
||||
settings_check_cleanup($pdo, $slug, $emailNorm);
|
||||
|
||||
if ($failures !== []) {
|
||||
echo "\nM3 settings flow check failed with " . count($failures) . " failure(s):\n";
|
||||
foreach ($failures as $failure) {
|
||||
echo "- {$failure}\n";
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "\nM3 settings flow check passed with {$passes} assertions.\n";
|
||||
@@ -61,6 +61,11 @@ $checks = [
|
||||
'path' => 'register.php',
|
||||
'contains' => ['Registrierung', 'Kundenkonto anlegen'],
|
||||
],
|
||||
[
|
||||
'label' => 'Mandant-Einstellungen Login-Schutz',
|
||||
'path' => 'mandant-einstellungen.php',
|
||||
'contains' => ['Login'],
|
||||
],
|
||||
[
|
||||
'label' => 'Namensanpassung',
|
||||
'path' => 'namenanpassen.php',
|
||||
|
||||
Reference in New Issue
Block a user