UserPaymentOptionStatus::class, 'grandfathered_until' => 'date', 'legacy_conditions' => 'array', 'current_period_start' => 'date', 'current_period_end' => 'date', 'cancelled_at' => 'datetime', ]; } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function paymentOption(): BelongsTo { return $this->belongsTo(PaymentOption::class); } public function companies(): BelongsToMany { return $this->belongsToMany(Company::class, 'user_payment_option_company') ->withPivot('is_active') ->withTimestamps(); } public function payments(): HasMany { return $this->hasMany(UserPayment::class); } /** * Aktuell gültige Zahlungsoption (period-aware): aktives Abo innerhalb der * laufenden Periode ODER laufender Bestandsschutz. Single Source of Truth * für „aktive Zahlungsoption" in API-Gate, Buchungs- und Kontingent-Checks. */ public function scopeActiveNow(Builder $query): Builder { return $query->where(function (Builder $query): void { $query ->where(function (Builder $active): void { $active ->where('status', UserPaymentOptionStatus::Active->value) ->whereDate('current_period_start', '<=', now()) ->whereDate('current_period_end', '>=', now()); }) ->orWhere(fn (Builder $grandfathered): Builder => $grandfathered->grandfatheredNow()); }); } /** * Laufender Bestandsschutz (Grandfathered mit gültigem grandfathered_until). */ public function scopeGrandfatheredNow(Builder $query): Builder { return $query ->where('status', UserPaymentOptionStatus::Grandfathered->value) ->whereDate('grandfathered_until', '>=', now()); } }