Kundenabo-Vorschau: bereits abgerechnete Punkte fehlten in der Basis

Die Kachelreihe "Punkte aus meinen Kundenabos zum 5./10./20./25." startete bei
einer Basis von 0 und addierte nur die noch offenen Ausfuehrungen. Damit standen
die bereits vergangenen Termine auf null, obwohl im Monat laengst Lieferungen
gebucht waren, und die spaeteren Termine zeigten nur den Rest statt des
Monatsstands.

Berater 454 im September: vorher 0 / 0 / 131 / 319, jetzt 457 / 457 / 588 / 776.
Betrifft im September 86 Berater mit zusammen 15.936 Punkten — reine Anzeige,
gebucht und abgerechnet wurde korrekt.

Die Basis ist jetzt die im Monat aus den eigenen Kundenabos abgerechnete
Punktzahl, ueber dieselbe Abgrenzung wie die Backoffice-Statistik
(SalesVolumePoints::customerAboPoints(), Stornos vorzeichengetreu). Damit liest
sich die Kachel wie die Prognose-Kachel auf dem Dashboard: Stand heute plus das,
was bis zum Termin noch hereinkommt. Hinweistext in allen vier Sprachen
nachgezogen.

Nicht aus dem Audit-Paket: Die Null-Basis stammt aus der K9-Umsetzung vom 07.09.,
nicht aus der A13-Korrektur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin 2026-09-11 07:36:13 +00:00
parent ff0860fb81
commit cb5664a483
7 changed files with 132 additions and 10 deletions

View file

@ -164,10 +164,12 @@ class PaylineForecastService
* des Beraters (Kundenwunsch 2026-09-07: „wieviel Volumenpunkte habe ich am
* 5. / 10. / 20. / 25. — nur mit meinen Kundenabos").
*
* Bewusst ohne das eigene Abo und ohne Payline: Basis ist 0, die Kacheln
* zeigen also die Punkte, die aus den eigenen Kundenabos bis zum jeweiligen
* Termin noch hereinkommen. Das ist ein anderer Zuschnitt als
* {@see forecastDays()}, wo eigene Abos in einer Team-Zahl mitlaufen.
* Bewusst ohne das eigene Abo und ohne Payline — aber MIT dem, was im Monat
* aus den Kundenabos schon abgerechnet ist. Die Basis war zuvor 0; damit
* standen die bereits gelieferten Termine auf null, obwohl die Punkte
* langst gebucht waren, und die spateren Termine zeigten nur den Rest statt
* des Monatsstands. Gleiche Lesart wie {@see forecastDays()} auf dem
* Dashboard: Stand heute plus das, was bis zum Termin noch hereinkommt.
*
* @return array{base: float, days: array<int, array{day: int, date: string, upcoming: float, total: float, is_past: bool}>}
*/
@ -184,12 +186,34 @@ class PaylineForecastService
->with('user_abo_items.product')
->get();
$base = $this->billedCustomerAboPoints($user, (int) $today->month, (int) $today->year);
return [
'base' => 0.0,
'days' => $this->cumulateByDeliveryDay($abos, 0.0, $today),
'base' => $base,
'days' => $this->cumulateByDeliveryDay($abos, $base, $today),
];
}
/**
* Im Monat bereits abgerechnete Punkte aus den EIGENEN Kundenabos des
* Beraters — die Basis der Kundenabo-Terminvorschau.
*
* Nutzt dieselbe Abgrenzung wie die Backoffice-Statistik
* ({@see SalesVolumePoints::customerAboPoints()}): Shop-/Kundenbuchungen aus
* Abo-Bestellungen, Stornos vorzeichengetreu. Das eigene Abo des Beraters
* bleibt draussen, es laeuft nicht ueber dieses Praedikat.
*/
private function billedCustomerAboPoints(User $user, int $month, int $year): float
{
return (float) UserSalesVolume::query()
->leftJoin('shopping_orders', 'shopping_orders.id', '=', 'user_sales_volumes.shopping_order_id')
->where('user_sales_volumes.user_id', $user->id)
->where('user_sales_volumes.month', $month)
->where('user_sales_volumes.year', $year)
->selectRaw(SalesVolumePoints::customerAboPoints().' as points')
->value('points');
}
/**
* Offene Ausführungen auf die vier Liefertermine kumulieren: je Termin die
* Punkte aller Abos, deren nächste Ausführung bis einschließlich zu diesem