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);
|
||||
|
||||
Reference in New Issue
Block a user