UserPaymentOptionStatus::class, 'grandfathered_until' => 'date', 'legacy_conditions' => 'array', 'current_period_start' => 'date', 'current_period_end' => 'date', 'cancelled_at' => 'datetime', ]; } /** * @return BelongsTo */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * @return BelongsTo */ public function paymentOption(): BelongsTo { return $this->belongsTo(PaymentOption::class); } /** * @return BelongsToMany */ public function companies(): BelongsToMany { return $this->belongsToMany(Company::class, 'user_payment_option_company') ->withPivot('is_active') ->withTimestamps(); } /** * @return HasMany */ 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_until` ist das explizite * Legacy-Vertragsende (valid_until_date) und deckelt, wenn gesetzt. * Bei offenen Dauervereinbarungen ist es NULL (Regelfall im Import) — * dann zählt die laufende, vom MAN-Rechnungslauf fortgerollte Periode * (`current_period_end` = next_due_date). Fix 06.07.2026: vorher galt * NULL pauschal als abgelaufen, wodurch praktisch kein Legacy-Kunde * eine aktive Zahlungsoption hatte („Buchung erforderlich"). */ public function scopeGrandfatheredNow(Builder $query): Builder { return $query ->where('status', UserPaymentOptionStatus::Grandfathered->value) ->where(function (Builder $query): void { $query ->whereDate('grandfathered_until', '>=', now()) ->orWhere(function (Builder $rolling): void { $rolling ->whereNull('grandfathered_until') ->whereDate('current_period_end', '>=', now()); }); }); } }