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>
23 lines
694 B
PHP
23 lines
694 B
PHP
<?php
|
|
|
|
namespace App\Services\Database;
|
|
|
|
use Illuminate\Database\DetectsConcurrencyErrors;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Erkennt Deadlocks und Lock-Wait-Timeouts der Datenbank — dieselbe Pruefung,
|
|
* mit der `DB::transaction($callback, $attempts)` einen Wiederholungsversuch
|
|
* ausloest. Nach einem Deadlock hat InnoDB die gesamte Transaktion bereits
|
|
* zurueckgerollt; ein solcher Fehler darf deshalb nirgends stillschweigend
|
|
* abgefangen werden, sondern muss bis zum Transaktions-Wrapper durchschlagen.
|
|
*/
|
|
class ConcurrencyError
|
|
{
|
|
use DetectsConcurrencyErrors;
|
|
|
|
public static function isCausedBy(Throwable $e): bool
|
|
{
|
|
return (new self)->causedByConcurrencyError($e);
|
|
}
|
|
}
|