mivita/app/Services/Backoffice/SalesVolumePoints.php
Kevin 54bf2e5595 Backoffice-Statistik: Punkte-Fix (kumulative Snapshots) & Meine Statistik
Punkte/Umsatz korrekt aus per-Zeile points/total_net je status berechnen
statt SUM der kumulativen month_*-Snapshots (zaehlte massiv mehrfach).
Zentralisiert in SalesVolumePoints, genutzt von Dashboard- & Drilldown-Service.

Detail-Tabelle neu gegliedert: Eigenpunkte | Shop | Abo | Sonstige |
Kundenpunkte (=Summe) | Gesamt, mit gruener Hervorhebung der Basis-/Summenspalten.

Neue VIP-Seite "Meine Statistik" (Untermenue neben Team-Statistik):
KPI-Karten + nachvollziehbare Detailtabellen, gruppiert nach eigenen
Bestellungen, Shop-Bestellungen und Abos (PersonalStatisticsService).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:31:14 +00:00

71 lines
2.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services\Backoffice;
/**
* Liefert SQL-Aggregat-Ausdrücke für die monatliche Punkte-/Umsatzauswertung
* der Backoffice-Statistik.
*
* Wichtig: In `user_sales_volumes` existiert pro Bestellung eine Zeile, und die
* Spalten `month_KP_points` / `month_shop_points` / `month_*_total_net` sind der
* kumulative Monats-Laufstand zum Zeitpunkt der Bestellung – KEINE Einzelbeträge.
* Eine SUM() über diese Spalten zählt mehrfach. Korrekt wird je Zeile die
* `points`/`total_net`-Spalte summiert und nach `status` gefiltert – analog zu
* {@see \App\Services\BusinessPlan\SalesPointsVolume::reCalculateSalesPointsVolume()}.
*
* Status-Zuordnung:
* - Eigenpunkte (KP): status 1, 4, 5, 6
* - Shop-/Kundenpunkte: status 2 (sowie status 6 mit status_turnover = 2)
* - status 0 (nicht zugeordnet) und 3 (pending) zählen nicht
*
* Alle Spalten werden voll qualifiziert, da `shopping_orders` ebenfalls eine
* `points`-Spalte besitzt (Join-Mehrdeutigkeit).
*/
class SalesVolumePoints
{
private const OWN_PREDICATE = 'user_sales_volumes.status IN (1, 4, 5, 6)';
private const SHOP_PREDICATE = '(user_sales_volumes.status = 2 OR (user_sales_volumes.status = 6 AND user_sales_volumes.status_turnover = 2))';
private const TURNOVER_PREDICATE = 'user_sales_volumes.status IN (1, 2, 4, 5, 6)';
public static function ownPoints(): string
{
return self::sumWhere(self::OWN_PREDICATE, 'user_sales_volumes.points');
}
public static function externalPoints(): string
{
return self::sumWhere(self::SHOP_PREDICATE, 'user_sales_volumes.points');
}
public static function customerAboPoints(): string
{
return self::sumWhere(self::SHOP_PREDICATE.' AND shopping_orders.is_abo = 1', 'user_sales_volumes.points');
}
public static function customerSingleOrderPoints(): string
{
return self::sumWhere(self::SHOP_PREDICATE.' AND shopping_orders.id IS NOT NULL AND (shopping_orders.is_abo = 0 OR shopping_orders.is_abo IS NULL)', 'user_sales_volumes.points');
}
public static function customerOtherPoints(): string
{
return self::sumWhere(self::SHOP_PREDICATE.' AND shopping_orders.id IS NULL', 'user_sales_volumes.points');
}
public static function totalPoints(): string
{
return self::ownPoints().' + '.self::externalPoints();
}
public static function turnoverNet(): string
{
return self::sumWhere(self::TURNOVER_PREDICATE, 'user_sales_volumes.total_net');
}
private static function sumWhere(string $predicate, string $column): string
{
return 'COALESCE(SUM(CASE WHEN '.$predicate.' THEN '.$column.' ELSE 0 END), 0)';
}
}