currentPlan(); $subscription = $user->subscription(); $hasSubscription = (bool) $subscription?->valid(); $billingInterval = null; if ($plan instanceof Plan && $subscription?->stripe_price) { $billingInterval = $subscription->stripe_price === $plan->stripe_price_id_yearly ? 'yearly' : 'monthly'; } $hasGrandfathered = $user->userPaymentOptions() ->grandfatheredNow() ->exists(); return new self( tier: $user->currentTier(), planName: $plan?->name, billingInterval: $billingInterval, hasActiveBooking: $user->hasActiveBooking(), hasSubscription: $hasSubscription, hasGrandfathered: $hasGrandfathered, openSinglePurchases: $user->singlePurchases()->grantingSubmission()->count(), unlimitedQuota: $user->hasUnlimitedPressReleaseQuota(), quotaTotal: $user->pressReleaseQuotaTotal(), quotaRemaining: $user->pressReleaseQuotaRemaining(), creditBalance: $user->creditBalance(), ); } public function hasCredits(): bool { return $this->creditBalance > 0; } /** * Menschlich lesbares Etikett der aktuellen Buchung. */ public function bookingLabel(): string { if ($this->planName !== null) { return $this->planName; } if ($this->hasGrandfathered) { return __('Bestandstarif'); } if ($this->openSinglePurchases > 0) { return trans_choice( '{1}:count Einzelbuchung|[2,*]:count Einzelbuchungen', $this->openSinglePurchases, ['count' => $this->openSinglePurchases], ); } return __('Kein Abo'); } public function billingIntervalLabel(): ?string { return match ($this->billingInterval) { 'monthly' => __('monatlich'), 'yearly' => __('jährlich'), default => null, }; } /** * Kompakter Text für das Trigger-Badge in der Topbar. */ public function badgeLabel(): string { $parts = []; $parts[] = $this->unlimitedQuota ? __(':icon PM', ['icon' => '∞']) : trans_choice('{0}:count PM|{1}:count PM|[2,*]:count PM', (int) $this->quotaRemaining, ['count' => (int) $this->quotaRemaining]); if ($this->hasCredits()) { $parts[] = __(':count Cr', ['count' => $this->creditBalance]); } return implode(' · ', $parts); } }