Recruiting Challenge: Rangliste nach Umsatz der Neupartner, Steuerpflicht

- 50 EUR je Neupartner bleiben; die Top 3 (500/300/200 EUR) ermittelt
  jetzt der Umsatz der Neupartner (wie Montenegro, ohne Einmalpunkte/Abos)
- Gleichstand bei Umsatzwertung: vorn liegt, wessen letzte gezaehlte
  Umsatzbuchung frueher kam
- Challenge-Seite: Praemie und Top-3-Darstellung auch bei Umsatzwertung,
  Ranglisten-Hinweis auf Umsatz (DE/EN/ES/FR)
- Seeder: neue Texte und Bedingungen (Rangliste, Steuerpflicht,
  vergünstigter Einstieg bis 31.10.), gleicht Wertung beim Einspielen ab

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-10-08 16:19:30 +00:00
parent 0a3eb791d4
commit fe1a9478f4
12 changed files with 239 additions and 79 deletions

View file

@ -400,6 +400,23 @@ class Incentive extends Model
return ! $this->includesSalesPoints() && ! $this->usesAbos() && $this->points_partner_onetime > 0;
}
/**
* Rangliste nach Umsatzpunkten der Neupartner: keine Einmal- und keine Abopunkte
* (Recruiting-Challenge ab 08.10.2026, Berechnung wie Montenegro).
*/
public function ranksBySalesOfNewPartners(): bool
{
return $this->includesSalesPoints() && ! $this->usesAbos() && $this->points_partner_onetime <= 0;
}
/**
* Darstellung als Challenge mit Geldpraemie (Praemie je Neupartner, Top-Bonus) statt Punktesystem.
*/
public function isCashChallenge(): bool
{
return $this->hasCashPayout() && ($this->ranksByPartnerCount() || $this->ranksBySalesOfNewPartners());
}
/**
* Get specific translation for a field and locale.
*/

View file

@ -13,6 +13,7 @@ use App\Models\UserAboOrder;
use App\Models\UserSalesVolume;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class IncentiveTracker
@ -414,9 +415,37 @@ class IncentiveTracker
}
}
if ($incentive->includesSalesPoints()) {
foreach (self::latestCountedSalesVolumeAt($participantIds) as $participantId => $timestamp) {
$reached[$participantId] = max($reached[$participantId] ?? 0, Carbon::parse($timestamp)->getTimestamp());
}
}
return $reached;
}
/**
* Juengste gezaehlte Umsatzbuchung je Teilnehmer, gemessen am Buchungszeitpunkt des
* Sales Volumes (bleibt beim Neuaufbau der Logs stabil).
*
* @param list<int> $participantIds
* @return Collection<int, string> Teilnehmer-ID => Zeitstempel
*/
private static function latestCountedSalesVolumeAt(array $participantIds): Collection
{
$logTable = (new IncentivePointsLog)->getTable();
$volumeTable = (new UserSalesVolume)->getTable();
return IncentivePointsLog::query()
->join($volumeTable, "{$volumeTable}.id", '=', "{$logTable}.user_sales_volume_id")
->whereIn("{$logTable}.participant_id", $participantIds)
->where("{$logTable}.is_storno", false)
->where("{$logTable}.points_accumulated", '>', 0)
->groupBy("{$logTable}.participant_id")
->selectRaw("{$logTable}.participant_id as participant_id, MAX({$volumeTable}.created_at) as reached_at")
->pluck('reached_at', 'participant_id');
}
/**
* Log-Eintrag schreiben (Audit-Trail, keine Duplikate).
*/