Kundenabo-Seite: Vorschau taggenau, Liste mit den tatsaechlich gebuchten Punkten
Vorschau und Liste liessen sich nicht gegeneinander nachrechnen — zwei Ursachen,
beide hier behoben.
1. Die Vorschau nahm eine pauschale Monatssumme als Basis. Damit enthielt die
Kachel zum 5. auch schon die Lieferungen vom 10. Jetzt zaehlt je Termin, was
bis zu diesem Tag gebucht war, plus die bis dahin offenen Lieferungen.
Buchungen nach dem letzten Liefertag (Nachzuegler, manuelle Gutschriften)
haengen sich an die letzte Kachel, damit die den Monatsstand zeigt. Jede
Kachel weist ihre Aufschluesselung aus ("457 abgerechnet + 131 offen").
Beraterin 454 im September: 132 / 457 / 588 / 776 statt 0 / 0 / 131 / 319.
2. Die Liste zeigte nur den wiederkehrenden Abo-Inhalt. Einmal-Artikel und
sonstige Zubuchungen einer Lieferung werden nach der Ausfuehrung aus dem Abo
entfernt und tauchten deshalb nirgends mehr auf — bei Abo 266 waren das 43
Punkte, bei Abo 418 rund 39. Neue Spalte "Diesen Monat" mit den je Abo
tatsaechlich gebuchten Punkten, bei Abweichung mit dem Zusatz "+43 einmalig",
dazu eine Summenzeile. Ein nicht zuordenbarer Rest (geloeschtes Abo, Buchung
ohne Ausfuehrungsbezug) wird gesondert ausgewiesen, damit die Addition aufgeht.
Die Spalte erscheint nur ohne Monatsfilter; fuer einen vergangenen Monat waere
sie irrefuehrend.
Summe der Monatsspalte = Basis der letzten Kachel; Summe der Abo-Punkte der Abos
mit Termin 20./25. = deren offener Teil.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cb5664a483
commit
685f7a90b9
11 changed files with 458 additions and 59 deletions
|
|
@ -3,7 +3,18 @@
|
|||
nach ihrer Vorgabe: Kunde, Liefertag, Abo-Punkte, nächste Ausführung,
|
||||
Startdatum, Ausführungen.
|
||||
|
||||
Erwartet $listAbos (Collection<UserAbo>) und $customerAboForecast|null. --}}
|
||||
Erwartet $listAbos (Collection<UserAbo>), $customerAboForecast|null sowie
|
||||
$customerAboBilled|null (user_abo_id => im Monat abgerechnete Punkte) und
|
||||
$customerAboBilledTotal|null. Die beiden letzten sind null, sobald auf einen
|
||||
anderen Monat gefiltert wird — dann waere eine Monatsspalte irrefuehrend. --}}
|
||||
@php
|
||||
// Defensiv: die Teilansicht wird auch aus Kontexten eingebunden, die keine
|
||||
// Monatswerte kennen (gefilterter Monat, Tests).
|
||||
$customerAboForecast = $customerAboForecast ?? null;
|
||||
$customerAboBilled = $customerAboBilled ?? null;
|
||||
$customerAboBilledTotal = $customerAboBilledTotal ?? null;
|
||||
@endphp
|
||||
|
||||
@if (! empty($customerAboForecast['days']))
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
|
|
@ -25,6 +36,16 @@
|
|||
<div class="small {{ $forecastDay['upcoming'] > 0 ? 'text-success' : 'text-muted' }}">
|
||||
{{ $forecastDay['is_past'] && $forecastDay['upcoming'] <= 0 ? __('home.forecast_done') : __('navigation.points') }}
|
||||
</div>
|
||||
@if ($forecastDay['upcoming'] != 0.0)
|
||||
{{-- Aufschluesselung, damit die Kachel gegen die Liste
|
||||
darunter nachrechenbar ist. --}}
|
||||
<div class="small text-muted mt-1">
|
||||
{{ __('abo.customer_abo_forecast_split', [
|
||||
'billed' => \App\Services\Util::formatNumber($forecastDay['billed'] ?? 0.0),
|
||||
'upcoming' => \App\Services\Util::formatNumber($forecastDay['upcoming']),
|
||||
]) }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -57,7 +78,10 @@
|
|||
<tr>
|
||||
<th data-sortable="true"><span class="sortable-label">{{ __('tables.customer') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
<th data-sortable="true" data-sort-type="number"><span class="sortable-label">{{ __('tables.abo_delivery_day') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
<th class="text-right" data-sortable="true" data-sort-type="number"><span class="sortable-label justify-content-end">{{ __('navigation.points') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
<th class="text-right" data-sortable="true" data-sort-type="number" title="{{ __('abo.customer_abo_points_recurring_hint') }}"><span class="sortable-label justify-content-end">{{ __('abo.customer_abo_points_recurring') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
@if ($customerAboBilled !== null)
|
||||
<th class="text-right" data-sortable="true" data-sort-type="number" title="{{ __('abo.customer_abo_points_billed_hint') }}"><span class="sortable-label justify-content-end">{{ __('abo.customer_abo_points_billed') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
@endif
|
||||
<th data-sortable="true" data-sort-type="date"><span class="sortable-label">{{ __('tables.next_date') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
<th data-sortable="true" data-sort-type="date"><span class="sortable-label">{{ __('tables.start_date') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
<th class="text-right" data-sortable="true" data-sort-type="number"><span class="sortable-label justify-content-end">{{ __('tables.abo_delivery') }} <i class="ion ion-ios-swap sort-icon"></i></span></th>
|
||||
|
|
@ -65,6 +89,17 @@
|
|||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@php
|
||||
// Fuer die Summenzeile: was steht in der Liste, und bleibt vom
|
||||
// Monatswert etwas uebrig, das keiner gelisteten Abo-Zeile zugeordnet
|
||||
// werden kann (geloeschtes Abo, Buchung ohne Ausfuehrungsbezug)?
|
||||
$billedShown = $customerAboBilled === null
|
||||
? 0.0
|
||||
: (float) $listAbos->sum(fn ($abo) => (float) ($customerAboBilled[$abo->id] ?? 0.0));
|
||||
$billedRest = $customerAboBilled === null
|
||||
? 0.0
|
||||
: round((float) $customerAboBilledTotal - $billedShown, 2);
|
||||
@endphp
|
||||
<tbody>
|
||||
@foreach ($listAbos as $listAbo)
|
||||
<tr>
|
||||
|
|
@ -87,6 +122,26 @@
|
|||
</td>
|
||||
<td data-sort-value="{{ (int) $listAbo->abo_interval }}">{{ \App\Services\HTMLHelper::getAboStrLang($listAbo->abo_interval) }}</td>
|
||||
<td class="text-right font-weight-bold text-primary" data-sort-value="{{ $listAbo->getTotalPoints() }}">{{ $listAbo->getFormattedTotalPoints() }}</td>
|
||||
@if ($customerAboBilled !== null)
|
||||
@php
|
||||
$billedPoints = (float) ($customerAboBilled[$listAbo->id] ?? 0.0);
|
||||
// Differenz zum wiederkehrenden Aboinhalt: Einmal-Artikel und
|
||||
// sonstige Zubuchungen der Lieferung, die danach wieder aus
|
||||
// dem Abo entfernt werden. Nur ausweisen, wenn das Abo diesen
|
||||
// Monat ueberhaupt geliefert hat.
|
||||
$extraPoints = $billedPoints != 0.0
|
||||
? round($billedPoints - (float) $listAbo->getTotalPoints(), 2)
|
||||
: 0.0;
|
||||
@endphp
|
||||
<td class="text-right" data-sort-value="{{ $billedPoints }}">
|
||||
<span class="font-weight-bold">{{ \App\Services\Util::formatNumber($billedPoints) }}</span>
|
||||
@if ($extraPoints != 0.0)
|
||||
<div class="small text-muted" title="{{ __('abo.customer_abo_points_extra_hint') }}">
|
||||
{{ $extraPoints > 0 ? '+' : '' }}{{ \App\Services\Util::formatNumber($extraPoints) }} {{ __('abo.customer_abo_points_extra') }}
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
<td data-sort-value="{{ $listAbo->getRawOriginal('next_date') }}">{{ $listAbo->next_date }}</td>
|
||||
<td data-sort-value="{{ $listAbo->getRawOriginal('start_date') }}">{{ $listAbo->start_date }}</td>
|
||||
<td class="text-right" data-sort-value="{{ $listAbo->getCountPaidOrders() }}">{{ $listAbo->getCountPaidOrders() }}</td>
|
||||
|
|
@ -99,6 +154,28 @@
|
|||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="font-weight-bold bg-light">
|
||||
<td colspan="2">{{ __('abo.customer_abo_list_sum') }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($listAbos->sum(fn ($abo) => (float) $abo->getTotalPoints())) }}</td>
|
||||
@if ($customerAboBilled !== null)
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($billedShown) }}</td>
|
||||
@endif
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
@if ($customerAboBilled !== null && $billedRest != 0.0)
|
||||
<tr class="small text-muted bg-light">
|
||||
<td colspan="3">{{ __('abo.customer_abo_list_sum_rest') }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($billedRest) }}</td>
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
<tr class="font-weight-bold bg-light">
|
||||
<td colspan="3">{{ __('abo.customer_abo_list_sum_month') }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber((float) $customerAboBilledTotal) }}</td>
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
@endif
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@php($abo_payment_links = $abo_payment_links ?? collect())
|
||||
@php
|
||||
$abo_payment_links = $abo_payment_links ?? collect();
|
||||
@endphp
|
||||
|
||||
@if($view === 'ot' && $abo_payment_links->isNotEmpty())
|
||||
@foreach($abo_payment_links as $paymentLink)
|
||||
|
|
@ -140,7 +142,19 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
@foreach($user_abos as $user_abo)
|
||||
@php
|
||||
// Kundenabos laufen ueber die sortierbare Liste; als Karte bleiben nur
|
||||
// die Ausnahmefaelle mit offener Erstzahlung, weil dort Aktionsbuttons
|
||||
// haengen (Kundenwunsch 2026-09-07).
|
||||
$cardAbos = $view === 'ot'
|
||||
? collect($user_abos)->filter(fn ($abo) => $abo->isPendingInitialPayment())
|
||||
: collect($user_abos);
|
||||
$listAbos = $view === 'ot'
|
||||
? collect($user_abos)->reject(fn ($abo) => $abo->isPendingInitialPayment())->values()
|
||||
: collect();
|
||||
@endphp
|
||||
|
||||
@foreach($cardAbos as $user_abo)
|
||||
<div class="card mb-4">
|
||||
@if($user_abo->isPendingInitialPayment())
|
||||
<div class="card-body pb-0">
|
||||
|
|
@ -262,6 +276,15 @@
|
|||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@if($view === 'ot' && $listAbos->isNotEmpty())
|
||||
@include('user.abo._customer_abo_list', [
|
||||
'listAbos' => $listAbos,
|
||||
'customerAboForecast' => $customerAboForecast ?? null,
|
||||
'customerAboBilled' => $customerAboBilled ?? null,
|
||||
'customerAboBilledTotal' => $customerAboBilledTotal ?? null,
|
||||
])
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue