Allow superadmin to bypass onboarding billing
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Filament\Resources\TenantResource\RelationManagers\PackagePurchasesRelat
|
||||
use App\Filament\Resources\TenantResource\RelationManagers\TenantPackagesRelationManager;
|
||||
use App\Filament\Resources\TenantResource\Schemas\TenantInfolist;
|
||||
use App\Jobs\AnonymizeAccount;
|
||||
use App\Models\Package;
|
||||
use App\Models\Tenant;
|
||||
use App\Notifications\InactiveTenantDeletionWarning;
|
||||
use App\Services\Audit\SuperAdminAuditLogger;
|
||||
@@ -205,11 +206,13 @@ class TenantResource extends Resource
|
||||
Forms\Components\Textarea::make('reason')->label('Grund')->rows(3),
|
||||
])
|
||||
->action(function (Tenant $record, array $data) {
|
||||
$package = Package::query()->find($data['package_id']);
|
||||
\App\Models\TenantPackage::create([
|
||||
'tenant_id' => $record->id,
|
||||
'package_id' => $data['package_id'],
|
||||
'expires_at' => $data['expires_at'],
|
||||
'active' => true,
|
||||
'price' => $package?->price ?? 0,
|
||||
'reason' => $data['reason'] ?? null,
|
||||
]);
|
||||
\App\Models\PackagePurchase::create([
|
||||
|
||||
@@ -103,6 +103,11 @@ class TenantPackage extends Model
|
||||
$tenantPackage->purchased_at = now();
|
||||
}
|
||||
|
||||
if ($tenantPackage->price === null) {
|
||||
$package = $tenantPackage->package ?? Package::query()->find($tenantPackage->package_id);
|
||||
$tenantPackage->price = $package?->price ?? 0;
|
||||
}
|
||||
|
||||
$package = $tenantPackage->package;
|
||||
|
||||
if ($package && $package->isReseller()) {
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -27,6 +28,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: true,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -40,6 +42,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -53,6 +56,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBe(ADMIN_BILLING_PATH);
|
||||
});
|
||||
@@ -66,6 +70,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -79,6 +84,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -92,6 +98,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -105,6 +112,7 @@ describe('resolveOnboardingRedirect', () => {
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: true,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
@@ -117,6 +125,21 @@ describe('resolveOnboardingRedirect', () => {
|
||||
pathname: '/event-admin/mobile/dashboard',
|
||||
isBillingPath: false,
|
||||
isOnboardingCompleted: true,
|
||||
isSuperAdmin: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for super admins without packages', () => {
|
||||
const result = resolveOnboardingRedirect({
|
||||
hasEvents: false,
|
||||
hasActivePackage: false,
|
||||
remainingEvents: null,
|
||||
pathname: '/event-admin/mobile/dashboard',
|
||||
isBillingPath: false,
|
||||
isOnboardingDismissed: false,
|
||||
isOnboardingCompleted: false,
|
||||
isSuperAdmin: true,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ type OnboardingRedirectInput = {
|
||||
isBillingPath: boolean;
|
||||
isOnboardingDismissed?: boolean;
|
||||
isOnboardingCompleted?: boolean;
|
||||
isSuperAdmin?: boolean;
|
||||
};
|
||||
|
||||
export function resolveOnboardingRedirect({
|
||||
@@ -21,7 +22,12 @@ export function resolveOnboardingRedirect({
|
||||
isBillingPath,
|
||||
isOnboardingDismissed,
|
||||
isOnboardingCompleted,
|
||||
isSuperAdmin,
|
||||
}: OnboardingRedirectInput): string | null {
|
||||
if (isSuperAdmin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isOnboardingDismissed || isOnboardingCompleted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -61,8 +61,15 @@ function RequireAuth() {
|
||||
const isWelcomePath = location.pathname.startsWith(ADMIN_WELCOME_BASE_PATH);
|
||||
const isBillingPath = location.pathname.startsWith(ADMIN_BILLING_PATH);
|
||||
const isTenantAdmin = Boolean(user && user.role !== 'member');
|
||||
const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'superadmin';
|
||||
const shouldCheckPackages =
|
||||
status === 'authenticated' && isTenantAdmin && !eventsLoading && !hasEvents && !isWelcomePath && !isBillingPath;
|
||||
status === 'authenticated'
|
||||
&& isTenantAdmin
|
||||
&& !isSuperAdmin
|
||||
&& !eventsLoading
|
||||
&& !hasEvents
|
||||
&& !isWelcomePath
|
||||
&& !isBillingPath;
|
||||
|
||||
const { data: packagesData, isLoading: packagesLoading } = useQuery({
|
||||
queryKey: ['mobile', 'onboarding', 'packages-overview'],
|
||||
@@ -93,6 +100,7 @@ function RequireAuth() {
|
||||
isBillingPath,
|
||||
isOnboardingDismissed,
|
||||
isOnboardingCompleted,
|
||||
isSuperAdmin,
|
||||
});
|
||||
|
||||
if (status === 'loading') {
|
||||
|
||||
30
tests/Feature/TenantPackageTest.php
Normal file
30
tests/Feature/TenantPackageTest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Package;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\TenantPackage;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TenantPackageTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_missing_price_defaults_to_package_price(): void
|
||||
{
|
||||
$tenant = Tenant::factory()->create();
|
||||
$package = Package::factory()->create([
|
||||
'price' => 123.45,
|
||||
]);
|
||||
|
||||
$tenantPackage = TenantPackage::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'package_id' => $package->id,
|
||||
'active' => true,
|
||||
])->refresh();
|
||||
|
||||
$this->assertSame('123.45', $tenantPackage->price);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user