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\RelationManagers\TenantPackagesRelationManager;
|
||||||
use App\Filament\Resources\TenantResource\Schemas\TenantInfolist;
|
use App\Filament\Resources\TenantResource\Schemas\TenantInfolist;
|
||||||
use App\Jobs\AnonymizeAccount;
|
use App\Jobs\AnonymizeAccount;
|
||||||
|
use App\Models\Package;
|
||||||
use App\Models\Tenant;
|
use App\Models\Tenant;
|
||||||
use App\Notifications\InactiveTenantDeletionWarning;
|
use App\Notifications\InactiveTenantDeletionWarning;
|
||||||
use App\Services\Audit\SuperAdminAuditLogger;
|
use App\Services\Audit\SuperAdminAuditLogger;
|
||||||
@@ -205,11 +206,13 @@ class TenantResource extends Resource
|
|||||||
Forms\Components\Textarea::make('reason')->label('Grund')->rows(3),
|
Forms\Components\Textarea::make('reason')->label('Grund')->rows(3),
|
||||||
])
|
])
|
||||||
->action(function (Tenant $record, array $data) {
|
->action(function (Tenant $record, array $data) {
|
||||||
|
$package = Package::query()->find($data['package_id']);
|
||||||
\App\Models\TenantPackage::create([
|
\App\Models\TenantPackage::create([
|
||||||
'tenant_id' => $record->id,
|
'tenant_id' => $record->id,
|
||||||
'package_id' => $data['package_id'],
|
'package_id' => $data['package_id'],
|
||||||
'expires_at' => $data['expires_at'],
|
'expires_at' => $data['expires_at'],
|
||||||
'active' => true,
|
'active' => true,
|
||||||
|
'price' => $package?->price ?? 0,
|
||||||
'reason' => $data['reason'] ?? null,
|
'reason' => $data['reason'] ?? null,
|
||||||
]);
|
]);
|
||||||
\App\Models\PackagePurchase::create([
|
\App\Models\PackagePurchase::create([
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ class TenantPackage extends Model
|
|||||||
$tenantPackage->purchased_at = now();
|
$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;
|
$package = $tenantPackage->package;
|
||||||
|
|
||||||
if ($package && $package->isReseller()) {
|
if ($package && $package->isReseller()) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -27,6 +28,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: true,
|
isBillingPath: true,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -40,6 +42,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -53,6 +56,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBe(ADMIN_BILLING_PATH);
|
expect(result).toBe(ADMIN_BILLING_PATH);
|
||||||
});
|
});
|
||||||
@@ -66,6 +70,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -79,6 +84,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -92,6 +98,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: false,
|
isOnboardingDismissed: false,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -105,6 +112,7 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingDismissed: true,
|
isOnboardingDismissed: true,
|
||||||
isOnboardingCompleted: false,
|
isOnboardingCompleted: false,
|
||||||
|
isSuperAdmin: false,
|
||||||
});
|
});
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -117,6 +125,21 @@ describe('resolveOnboardingRedirect', () => {
|
|||||||
pathname: '/event-admin/mobile/dashboard',
|
pathname: '/event-admin/mobile/dashboard',
|
||||||
isBillingPath: false,
|
isBillingPath: false,
|
||||||
isOnboardingCompleted: true,
|
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();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type OnboardingRedirectInput = {
|
|||||||
isBillingPath: boolean;
|
isBillingPath: boolean;
|
||||||
isOnboardingDismissed?: boolean;
|
isOnboardingDismissed?: boolean;
|
||||||
isOnboardingCompleted?: boolean;
|
isOnboardingCompleted?: boolean;
|
||||||
|
isSuperAdmin?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resolveOnboardingRedirect({
|
export function resolveOnboardingRedirect({
|
||||||
@@ -21,7 +22,12 @@ export function resolveOnboardingRedirect({
|
|||||||
isBillingPath,
|
isBillingPath,
|
||||||
isOnboardingDismissed,
|
isOnboardingDismissed,
|
||||||
isOnboardingCompleted,
|
isOnboardingCompleted,
|
||||||
|
isSuperAdmin,
|
||||||
}: OnboardingRedirectInput): string | null {
|
}: OnboardingRedirectInput): string | null {
|
||||||
|
if (isSuperAdmin) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (isOnboardingDismissed || isOnboardingCompleted) {
|
if (isOnboardingDismissed || isOnboardingCompleted) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,15 @@ function RequireAuth() {
|
|||||||
const isWelcomePath = location.pathname.startsWith(ADMIN_WELCOME_BASE_PATH);
|
const isWelcomePath = location.pathname.startsWith(ADMIN_WELCOME_BASE_PATH);
|
||||||
const isBillingPath = location.pathname.startsWith(ADMIN_BILLING_PATH);
|
const isBillingPath = location.pathname.startsWith(ADMIN_BILLING_PATH);
|
||||||
const isTenantAdmin = Boolean(user && user.role !== 'member');
|
const isTenantAdmin = Boolean(user && user.role !== 'member');
|
||||||
|
const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'superadmin';
|
||||||
const shouldCheckPackages =
|
const shouldCheckPackages =
|
||||||
status === 'authenticated' && isTenantAdmin && !eventsLoading && !hasEvents && !isWelcomePath && !isBillingPath;
|
status === 'authenticated'
|
||||||
|
&& isTenantAdmin
|
||||||
|
&& !isSuperAdmin
|
||||||
|
&& !eventsLoading
|
||||||
|
&& !hasEvents
|
||||||
|
&& !isWelcomePath
|
||||||
|
&& !isBillingPath;
|
||||||
|
|
||||||
const { data: packagesData, isLoading: packagesLoading } = useQuery({
|
const { data: packagesData, isLoading: packagesLoading } = useQuery({
|
||||||
queryKey: ['mobile', 'onboarding', 'packages-overview'],
|
queryKey: ['mobile', 'onboarding', 'packages-overview'],
|
||||||
@@ -93,6 +100,7 @@ function RequireAuth() {
|
|||||||
isBillingPath,
|
isBillingPath,
|
||||||
isOnboardingDismissed,
|
isOnboardingDismissed,
|
||||||
isOnboardingCompleted,
|
isOnboardingCompleted,
|
||||||
|
isSuperAdmin,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'loading') {
|
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