diff --git a/app/Livewire/Admin/Customers.php b/app/Livewire/Admin/Customers.php index f52d1e1..ce547e6 100644 --- a/app/Livewire/Admin/Customers.php +++ b/app/Livewire/Admin/Customers.php @@ -18,6 +18,12 @@ class Customers extends Component return; } + // A closed account is terminal — the suspend/reactivate toggle must not + // resurrect it to active while closed_at is still set. + if ($customer->closed_at !== null || $customer->status === 'closed') { + return; + } + $customer->update([ 'status' => $customer->status === 'suspended' ? 'active' : 'suspended', ]); @@ -46,8 +52,13 @@ class Customers extends Component 'plan' => $planKey !== null ? __('billing.plan.'.$planKey) : '—', 'mrr' => Number::currency($priceCents / 100, in: 'EUR', locale: $locale), 'instance' => $instance->subdomain ?? '—', + 'closed' => $c->closed_at !== null || $c->status === 'closed', 'suspended' => $c->status === 'suspended', - 'status' => $c->status === 'suspended' ? 'suspended' : ($instance->status ?? $c->status ?? 'provisioning'), + 'status' => match (true) { + $c->closed_at !== null || $c->status === 'closed' => 'closed', + $c->status === 'suspended' => 'suspended', + default => $instance->status ?? $c->status ?? 'provisioning', + }, ]; })->all(); diff --git a/app/Livewire/Users.php b/app/Livewire/Users.php index 00c0590..77ced0b 100644 --- a/app/Livewire/Users.php +++ b/app/Livewire/Users.php @@ -143,7 +143,10 @@ class Users extends Component private function seatLimit(Customer $customer): int { - $instance = $customer->instances()->latest('id')->first(); + // The entitlement follows the active (or cancelling) package, not a newer + // failed/deprovisioned record. + $instance = $customer->instances()->whereIn('status', ['active', 'cancellation_scheduled'])->latest('id')->first() + ?? $customer->instances()->latest('id')->first(); $plan = $instance->plan ?? 'start'; return (int) config("provisioning.plans.$plan.seats", 5); diff --git a/database/factories/CustomerFactory.php b/database/factories/CustomerFactory.php index 6a108ac..20877f8 100644 --- a/database/factories/CustomerFactory.php +++ b/database/factories/CustomerFactory.php @@ -14,7 +14,9 @@ class CustomerFactory extends Factory { return [ 'name' => $this->faker->company(), - 'email' => $this->faker->unique()->safeEmail(), + // Large unique space — safeEmail()'s pool can collide late in a full + // suite run (process-wide unique() state), tripping customers.email. + 'email' => 'customer-'.$this->faker->unique()->numerify('########').'@example.test', 'locale' => 'de', 'status' => 'active', ]; diff --git a/database/factories/SeatFactory.php b/database/factories/SeatFactory.php index 744a320..4ea4f6e 100644 --- a/database/factories/SeatFactory.php +++ b/database/factories/SeatFactory.php @@ -15,7 +15,7 @@ class SeatFactory extends Factory { return [ 'customer_id' => Customer::factory(), - 'email' => $this->faker->unique()->safeEmail(), + 'email' => 'seat-'.$this->faker->unique()->numerify('########').'@example.test', 'name' => $this->faker->name(), 'role' => 'member', 'status' => 'active', diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index c4ceb07..2a4ee9c 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -26,7 +26,7 @@ class UserFactory extends Factory { return [ 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), + 'email' => 'user-'.fake()->unique()->numerify('########').'@example.test', 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), diff --git a/lang/de/admin.php b/lang/de/admin.php index 1baad03..101a76f 100644 --- a/lang/de/admin.php +++ b/lang/de/admin.php @@ -87,6 +87,7 @@ return [ 'active' => 'Aktiv', 'provisioning' => 'Bereitstellung', 'suspended' => 'Ausgesetzt', + 'closed' => 'Geschlossen', 'warning' => 'Warnung', ], diff --git a/lang/en/admin.php b/lang/en/admin.php index 4fa41b3..5f5b427 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -87,6 +87,7 @@ return [ 'active' => 'Active', 'provisioning' => 'Provisioning', 'suspended' => 'Suspended', + 'closed' => 'Closed', 'warning' => 'Warning', ], diff --git a/resources/views/livewire/admin/customers.blade.php b/resources/views/livewire/admin/customers.blade.php index bffc8e6..56c1c98 100644 --- a/resources/views/livewire/admin/customers.blade.php +++ b/resources/views/livewire/admin/customers.blade.php @@ -29,11 +29,13 @@ {{ __('admin.status.'.$r['status']) }}
- + @unless ($r['closed']) + + @endunless
@csrf