Update partner packages, copy, and demo switcher
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-15 17:33:36 +01:00
parent 2f93271d94
commit ad829ae509
50 changed files with 1335 additions and 411 deletions

View File

@@ -11,7 +11,7 @@ class PackageLimitEvaluator
{
public function __construct(private readonly TenantUsageService $tenantUsageService) {}
public function assessEventCreation(Tenant $tenant): ?array
public function assessEventCreation(Tenant $tenant, ?string $includedPackageSlug = null): ?array
{
$hasEndcustomerPackage = $tenant->tenantPackages()
->where('active', true)
@@ -22,17 +22,66 @@ class PackageLimitEvaluator
return null;
}
if ($tenant->hasEventAllowance()) {
if ($tenant->hasEventAllowanceFor($includedPackageSlug)) {
return null;
}
$package = $tenant->getActiveResellerPackage();
$package = $tenant->getActiveResellerPackageFor($includedPackageSlug);
if (! $package) {
if ($includedPackageSlug) {
$hasAnyActive = $tenant->tenantPackages()
->where('active', true)
->where(function ($query) {
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->whereHas('package', fn ($query) => $query->withTrashed()->where('type', 'reseller'))
->exists();
if ($hasAnyActive) {
return [
'code' => 'event_tier_unavailable',
'title' => __('api.packages.event_tier_unavailable.title'),
'message' => __('api.packages.event_tier_unavailable.message'),
'status' => 402,
'meta' => [
'scope' => 'events',
'requested_tier' => $includedPackageSlug,
],
];
}
}
$latestResellerPackage = $tenant->tenantPackages()
->with('package')
->whereHas('package', fn ($query) => $query->withTrashed()->where('type', 'reseller'))
->orderByDesc('purchased_at')
->orderByDesc('id')
->first();
if ($latestResellerPackage && $latestResellerPackage->package) {
$limit = $latestResellerPackage->package->max_events_per_year ?? 0;
return [
'code' => 'event_limit_exceeded',
'title' => __('api.packages.event_limit_exceeded.title'),
'message' => __('api.packages.event_limit_exceeded.message'),
'status' => 402,
'meta' => [
'scope' => 'events',
'used' => (int) $latestResellerPackage->used_events,
'limit' => $limit,
'remaining' => max(0, $limit - $latestResellerPackage->used_events),
'tenant_package_id' => $latestResellerPackage->id,
'package_id' => $latestResellerPackage->package_id,
],
];
}
return [
'code' => 'event_limit_missing',
'title' => 'No package assigned',
'message' => 'Assign a package or addon to create events.',
'title' => __('api.packages.event_limit_missing.title'),
'message' => __('api.packages.event_limit_missing.message'),
'status' => 402,
'meta' => [
'scope' => 'events',
@@ -49,8 +98,8 @@ class PackageLimitEvaluator
return [
'code' => 'event_limit_exceeded',
'title' => 'Event quota reached',
'message' => 'Your current package has no remaining event slots. Please upgrade or renew your subscription.',
'title' => __('api.packages.event_limit_exceeded.title'),
'message' => __('api.packages.event_limit_exceeded.message'),
'status' => 402,
'meta' => [
'scope' => 'events',
@@ -74,8 +123,8 @@ class PackageLimitEvaluator
if (! $event) {
return [
'code' => 'event_not_found',
'title' => 'Event not accessible',
'message' => 'The selected event could not be found or belongs to another tenant.',
'title' => __('api.packages.event_not_found.title'),
'message' => __('api.packages.event_not_found.message'),
'status' => 404,
'meta' => [
'scope' => 'photos',
@@ -87,8 +136,8 @@ class PackageLimitEvaluator
if (! $eventPackage || ! $eventPackage->package) {
return [
'code' => 'event_package_missing',
'title' => 'Event package missing',
'message' => 'No package is attached to this event. Assign a package to enable uploads.',
'title' => __('api.packages.event_package_missing.title'),
'message' => __('api.packages.event_package_missing.message'),
'status' => 409,
'meta' => [
'scope' => 'photos',
@@ -102,8 +151,8 @@ class PackageLimitEvaluator
if ($maxPhotos !== null && $eventPackage->used_photos >= $maxPhotos) {
return [
'code' => 'photo_limit_exceeded',
'title' => 'Photo upload limit reached',
'message' => 'This event has reached its photo allowance. Upgrade the event package to accept more uploads.',
'title' => __('api.packages.photo_limit_exceeded.title'),
'message' => __('api.packages.photo_limit_exceeded.message'),
'status' => 402,
'meta' => [
'scope' => 'photos',
@@ -122,8 +171,8 @@ class PackageLimitEvaluator
if ($eventPackage->used_photos >= $tenantPhotoLimit) {
return [
'code' => 'tenant_photo_limit_exceeded',
'title' => 'Tenant photo limit reached',
'message' => 'This tenant has reached its photo allowance for the event.',
'title' => __('api.packages.tenant_photo_limit_exceeded.title'),
'message' => __('api.packages.tenant_photo_limit_exceeded.message'),
'status' => 402,
'meta' => [
'scope' => 'photos',
@@ -146,8 +195,8 @@ class PackageLimitEvaluator
if ($projectedBytes >= $storageLimitBytes) {
return [
'code' => 'tenant_storage_limit_exceeded',
'title' => 'Tenant storage limit reached',
'message' => 'This tenant has reached its storage allowance.',
'title' => __('api.packages.tenant_storage_limit_exceeded.title'),
'message' => __('api.packages.tenant_storage_limit_exceeded.message'),
'status' => 402,
'meta' => [
'scope' => 'storage',