Team-Statistik = Team-Struktur (Provisionsbaum, Umschalter) und Laufstand-Fix in user_sales_volumes
K11 aus dem September-Protokoll (Tickets C1 und C2 im Entwicklungsplan), live seit 14.09.2026. C1 – Statistik rechnet wie die Provision - Standard-Baum ist der Provisionsbaum (Mitglieder wie TreeCalcBot: Mitgliedsebene, Abrechnungskonto, active_date; ausgefilterte Knoten kappen ihren Unterbaum, max. 21 Linien). Gesamte Downline bleibt per Umschalter erreichbar, Wahl in der Session. - Punkte-Definition: Provisionspunkte (TP + Shop), reine Karrierepunkte ausgewiesen statt eingerechnet, negative Monatssalden je User wie in der Provision auf 0 gekappt. - Abgeschlossene Monate nehmen die Mitglieder je Linie aus dem gespeicherten Provisionslauf (user_businesses), Snapshots je Baum-Definition (Payload-Version 3, Migration). - Struktur-Modal: Gesamt aus den Linien-Punkten mit Dezimalen statt der int-Spalte total_pp. - Messlauf statistics:compare-trees (Downline / Provisionsbaum / Struktur je VIP, Laufstand-Drift). C2 – Laufstand-Race (Provision zu niedrig bei zeitgleichen Buchungen) - Neuberechnung liest die Zeilen des Beraters sperrend (FOR UPDATE), Index (user_id, year, month). - Paid-Pfad der Payone- und Stripe-Webhooks als DB::transaction mit 3 Anlaeufen; Deadlock und Lock-Timeout aus der Rechnungserstellung werden nicht mehr geschluckt. - Sicherheitsnetz sales:repair-running-totals taeglich 02:45 vor dem Provisionslauf; abgeschlossene Monate werden nicht angefasst (Entscheid 14.09.: keine Nachzahlung fuer 06/08). Doku: Entwicklungsplan zum Protokoll, Deploy-Notiz 14.09. (inkl. Migrations-Stolperstein: Migrationen tragen Werte als Literale, nicht als Konstanten aus dem Anwendungscode). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
685f7a90b9
commit
96545364f1
33 changed files with 2321 additions and 139 deletions
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Backoffice-Statistik: je Monat ein Snapshot pro Baum-Definition
|
||||
* (Provisionsbaum / gesamte Downline, Kundenentscheid 2026-09-14).
|
||||
*
|
||||
* Bestehende Snapshots wurden mit der kompletten Downline und der alten
|
||||
* Punkte-Definition gebaut — sie werden als `downline` gekennzeichnet. Die
|
||||
* Provisionsbaum-Snapshots der abgeschlossenen Monate legt der naechste Lauf von
|
||||
* `backoffice:store-statistics-snapshots` an (Deploy: einmal mit `--force`, damit
|
||||
* beide Varianten die neue Punkte-Definition tragen).
|
||||
*
|
||||
* Die Werte stehen bewusst als Literale hier (nicht als Konstanten aus
|
||||
* BackofficeDashboardService): eine Migration darf nicht vom Stand des
|
||||
* Anwendungscodes abhaengen — auf dem Server lief sie mit einer noch nicht
|
||||
* neu geladenen Klasse auf „Undefined constant".
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('backoffice_statistics_snapshots', function (Blueprint $table) {
|
||||
$table->string('tree', 16)->default('commission')->after('month');
|
||||
});
|
||||
|
||||
DB::table('backoffice_statistics_snapshots')->update(['tree' => 'downline']);
|
||||
|
||||
// Erst den neuen Unique-Index anlegen, dann den alten entfernen: der
|
||||
// Fremdschluessel auf user_id braucht in MySQL durchgehend einen Index,
|
||||
// der mit user_id beginnt.
|
||||
Schema::table('backoffice_statistics_snapshots', function (Blueprint $table) {
|
||||
$table->unique(['user_id', 'year', 'month', 'tree'], 'backoffice_statistics_snapshot_tree_unique');
|
||||
});
|
||||
|
||||
Schema::table('backoffice_statistics_snapshots', function (Blueprint $table) {
|
||||
$table->dropUnique('backoffice_statistics_snapshot_unique');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('backoffice_statistics_snapshots')
|
||||
->where('tree', '<>', 'downline')
|
||||
->delete();
|
||||
|
||||
Schema::table('backoffice_statistics_snapshots', function (Blueprint $table) {
|
||||
$table->unique(['user_id', 'year', 'month'], 'backoffice_statistics_snapshot_unique');
|
||||
});
|
||||
|
||||
Schema::table('backoffice_statistics_snapshots', function (Blueprint $table) {
|
||||
$table->dropUnique('backoffice_statistics_snapshot_tree_unique');
|
||||
$table->dropColumn('tree');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Laufstand-Race (Ticket C2): die Neuberechnung der Laufstaende liest die
|
||||
* Zeilen eines Beraters im Monat jetzt sperrend (SELECT ... FOR UPDATE). Mit
|
||||
* diesem Index sperrt InnoDB genau den Bereich (user_id, year, month) statt
|
||||
* aller Zeilen des Users ueber den Fremdschluessel-Index.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_sales_volumes', function (Blueprint $table) {
|
||||
$table->index(['user_id', 'year', 'month'], 'user_sales_volumes_user_year_month_index');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_sales_volumes', function (Blueprint $table) {
|
||||
$table->dropIndex('user_sales_volumes_user_year_month_index');
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue