mivita/resources/views/user/team/customer_abos.blade.php
Kevin d6efe56cd4 Abo (A3): Status und Grund eines Abos sichtbar machen
Neue Spalten user_abos.status_reason / paused_until und
user_abo_orders.error_message. Der Grund wird zentral am Model gepflegt
(saving-Event): Zahlungsfehler, Kuendigung/Deaktivierung mit Akteur aus dem
Formular-Kanal, Loeschen beim Zurueckkehren in den Lauf; die Pause setzt
paused_until und paused_by_*. Cron- und Retry-Zahlung schreiben den Fehlertext
des Anbieters an die Abo-Bestellung.

AboStatusExplainer uebersetzt das in einen Satz ("Pausiert bis ... vom Kunden
am ...", "Zahlung am ... fehlgeschlagen - Grund: ...", "Gekuendigt am ... vom
Berater"), Partial _status_explain in Abo-Detail (CRM, Portal, Admin),
Abo-Uebersicht, Kundenabo-Liste, Team-Kundenabos und Admin-Datatable; dort
neuer Filter "Zustand" (pausiert / Zahlung fehlgeschlagen). Backfill-Kommando
abo:backfill-status-reasons fuer Bestandsdaten, Fallback ueber das
Statusprotokoll und die Pausenfelder.

Sprachdateien de/en/es/fr, Tests in AboStatusExplainTest, Plan-Status.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 10:24:18 +00:00

143 lines
8.3 KiB
PHP

@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2 d-flex justify-content-between align-items-center w-100">
<div>
{{ __('abo.team_customer_abos') }} / {{ __('navigation.overview') }}
</div>
</h4>
@if(isset($chartData))
@include('user.abo._abo_chart')
@endif
<div class="row">
<div class="col-12">
@if($groupedByMember->isEmpty())
<div class="card mb-4">
<div class="card-body">
<p class="text-muted mb-0">{{ __('tables.no_data_available') }}</p>
</div>
</div>
@else
@php $memberIndex = 0; @endphp
@foreach($groupedByMember as $memberId => $memberAbos)
@php
$member = $memberAbos->first()->member;
$memberName = $member && $member->account
? $member->account->first_name . ' ' . $member->account->last_name
: '#' . $memberId;
$totalPoints = $memberAbos->sum(fn($abo) => $abo->getTotalPoints());
$aboCount = $memberAbos->count();
$collapseId = 'member-abos-' . $memberId;
$memberIndex++;
@endphp
<div class="card mb-3">
<div class="card-body pb-2">
<div class="row align-items-center">
<div class="col-sm-6">
<h5 class="mb-0">
<a href="#" class="text-black" data-toggle="modal"
data-target="#modals-load-content"
data-id="{{ $memberId }}"
data-action="business-user-show"
data-back=""
data-modal="modal-md"
data-init_from="member"
data-route="{{ route('modal_load') }}">
<span class="mr-1 ion ion-ios-contact"></span>
{{ $memberName }}
</a>
</h5>
</div>
<div class="col-sm-6 text-right">
<button class="btn btn-sm btn-outline-secondary" type="button"
data-toggle="collapse" data-target="#{{ $collapseId }}"
aria-expanded="{{ $memberIndex === 1 ? 'true' : 'false' }}">
<span class="ion ion-ios-list mr-1"></span>
{{ $aboCount }} {{ __('abo.abo') }}
</button>
</div>
</div>
</div>
<hr class="m-0">
<div class="card-body py-2">
<div class="row">
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('abo.abo_count') }}:</span>
<strong class="ml-1">{{ $aboCount }}</strong>
</div>
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('navigation.points') }}:</span>
<strong class="ml-1 text-primary">{{ \App\Services\Util::formatNumber($totalPoints) }} Pkt.</strong>
</div>
<div class="col-md-4 mb-1">
<span class="text-muted small">{{ __('tables.active') }}:</span>
<strong class="ml-1">{{ $memberAbos->where('active', true)->count() }} / {{ $aboCount }}</strong>
</div>
</div>
</div>
<div class="collapse {{ $memberIndex === 1 ? 'show' : '' }}" id="{{ $collapseId }}">
<hr class="m-0">
<div class="card-body pt-2 pb-1">
<p class="text-muted small mb-2">
<span class="ion ion-ios-lock mr-1"></span>
{{ __('abo.customer_privacy_info') }}
</p>
<div class="table-responsive">
<table class="table table-sm table-borderless mb-0">
<thead>
<tr class="text-muted small">
<th>{{ __('tables.customer') }}</th>
<th>{{ __('navigation.points') }}</th>
<th>{{ __('tables.status') }}</th>
<th>{{ __('tables.next_date') }}</th>
<th>{{ __('tables.abo_delivery') }}</th>
<th>{{ __('tables.active') }}</th>
</tr>
</thead>
<tbody>
@foreach($memberAbos as $index => $abo)
<tr>
<td>
<span class="badge badge-outline-secondary">
{{ __('tables.customer') }} #{{ $index + 1 }}
</span>
@if($abo->isPendingInitialPayment())
<div class="small text-warning mt-1">
@if($abo->hasFailedInitialPayment())
{{ __('abo.pending_payment_failed_title') }}
@elseif($abo->hasInitialPaymentStatus())
{{ __('abo.pending_payment_processing_title') }}
@else
{{ __('abo.pending_payment_waiting_title') }}
@endif
</div>
@endif
</td>
<td>
<strong class="text-primary">
{{ $abo->getFormattedTotalPoints() }} Pkt.
</strong>
</td>
<td>@include('admin.abo._status_explain', ['abo' => $abo, 'compact' => true])</td>
<td>{{ $abo->next_date }}</td>
<td>{{ $abo->getCountOrders() }}</td>
<td>{!! get_active_badge($abo->active) !!}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endforeach
@endif
</div>
</div>
@endsection