Abo: Kreditkarte aktualisieren (CRM, Portal, Berater-Link) + Admin-Abo-Löschung

- Karten-Update ohne Abbuchung via PAYONE creditcardcheck/pseudocardpan
  (AboPaymentMethodService, CardUpdateResult/CreditCardCheckData-DTOs)
- Eingabe nur durch Abo-Inhaber: CRM- und Portal-Formular + zeitlich
  begrenzter Berater→Kunde-Link (AboCardUpdateLink + Mail)
- Änderungshistorie in user_abo_card_changes, Anzeige im Admin-Abo-Detail
- Admin: Abo löschen (destroy) inkl. Test
- Portal-Fix: myOrderCreate wirft 404 statt TypeError bei ungültiger ID
- VIP-Sichtbarkeitstests für Einmalartikel-Fenster ergänzt
- Enthält bereits die Freeze-Guards für den Stripe-Notbetrieb (Folge-Commit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-07-08 15:56:19 +00:00
parent 63c36555a5
commit 671022a28d
36 changed files with 2366 additions and 26 deletions

View file

@ -6,11 +6,14 @@
namespace App\Models;
use App\Services\Backoffice\BackofficeDashboardService;
use App\Services\Util;
use App\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
@ -43,9 +46,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $last_date
* @property \Illuminate\Support\Carbon|null $next_date
* @property-read User|null $member
* @property-read \App\Models\ShoppingUser $shopping_user
* @property-read Collection<int, \App\Models\UserAboItem> $user_abo_items
* @property-read Collection<int, \App\Models\UserAboOneTimeItem> $one_time_items
* @property-read ShoppingUser $shopping_user
* @property-read Collection<int, UserAboItem> $user_abo_items
* @property-read Collection<int, UserAboOneTimeItem> $one_time_items
* @property-read int|null $user_abo_items_count
* @property-read int|null $user_abo_orders_count
*
@ -113,6 +116,9 @@ class UserAbo extends Model
'is_for',
'email',
'payone_userid',
'payment_provider',
'stripe_customer_id',
'stripe_payment_method_id',
'clearingtype',
'wallettype',
'carddata',
@ -158,9 +164,9 @@ class UserAbo extends Model
/**
* Aktive Abos (laufend): active=true und kein terminaler/inaktiver Status
* (4 = storniert, 5 = beendet, 6 = inaktiv). Spiegelt die Definition aus
* {@see \App\Services\Backoffice\BackofficeDashboardService}.
* {@see BackofficeDashboardService}.
*/
public function scopeActiveAbo(\Illuminate\Database\Eloquent\Builder $query): \Illuminate\Database\Eloquent\Builder
public function scopeActiveAbo(Builder $query): Builder
{
return $query
->where('active', true)
@ -172,7 +178,7 @@ class UserAbo extends Model
* sind (Eingangsmonat = start_date). Bei month=0 (oder außerhalb 1–12) bleibt die
* Abfrage unverändert.
*/
public function scopeAddedInMonth(\Illuminate\Database\Eloquent\Builder $query, int $year, int $month): \Illuminate\Database\Eloquent\Builder
public function scopeAddedInMonth(Builder $query, int $year, int $month): Builder
{
if ($month < 1 || $month > 12) {
return $query;
@ -301,7 +307,7 @@ class UserAbo extends Model
return $pan;
}
public function card_changes(): \Illuminate\Database\Eloquent\Relations\HasMany
public function card_changes(): HasMany
{
return $this->hasMany(UserAboCardChange::class)->latest();
}