mivita/dev/code/Services/BusinessPlan/BusinessUserItemOptimized.php
2025-08-12 18:01:59 +02:00

461 lines
No EOL
16 KiB
PHP

<?php
namespace App\Services\BusinessPlan;
use App\User;
use stdClass;
use Carbon\Carbon;
use App\Models\UserLevel;
use App\Models\UserBusiness;
use App\Services\TranslationHelper;
use App\Models\UserBusinessStructure;
/**
* Optimierte Version der BusinessUserItem Klasse
*
* Hauptverbesserungen:
* - makeUserFromModel() für bereits geladene User-Objekte
* - Bessere Error-Behandlung mit Logging
* - Optimierte Datenbankzugriffe durch Relations-Nutzung
* - Input-Validierung und Boundary-Checks
*/
class BusinessUserItemOptimized
{
public $businessUserItems = [];
private $date;
private $b_user;
private $user_level_active_pos;
public function __construct($date)
{
$this->date = $date;
return $this;
}
/**
* Erstellt BusinessUser aus User-ID (Original-Methode für Rückwärtskompatibilität)
*/
public function makeUser($user_id): void
{
try {
// Prüfe ob bereits gespeicherte Business-Daten existieren
$this->b_user = UserBusiness::where('user_id', $user_id)
->where('month', $this->date->month)
->where('year', $this->date->year)
->first();
if ($this->b_user !== null) {
return; // Bereits gespeicherte Daten verwenden
}
// Lade User mit Relations (weniger effizient als makeUserFromModel)
$user = User::with(['account', 'userLevel'])->find($user_id);
if (!$user) {
\Log::warning("BusinessUserItem: User not found: {$user_id}");
return;
}
$this->initializeFromUserModel($user);
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error creating user {$user_id}: " . $e->getMessage());
throw $e;
}
}
/**
* NEUE OPTIMIERTE METHODE: Erstellt BusinessUser aus bereits geladenem User-Objekt
* Nutzt bereits geladene Relations und vermeidet zusätzliche DB-Abfragen
*/
public function makeUserFromModel(User $user): void
{
try {
if (!$user || !$user->id) {
throw new \InvalidArgumentException('Invalid user model provided');
}
// Prüfe ob bereits gespeicherte Business-Daten existieren
$existingBusiness = null;
if ($user->relationLoaded('userBusiness')) {
$existingBusiness = $user->userBusiness->first();
}
if ($existingBusiness) {
$this->b_user = $existingBusiness;
return;
}
$this->initializeFromUserModel($user);
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error creating user from model {$user->id}: " . $e->getMessage());
throw $e;
}
}
/**
* Initialisiert BusinessUser aus User-Model (gemeinsame Logik)
*/
private function initializeFromUserModel(User $user): void
{
// Nutze geladene Relations wenn verfügbar
$user_level_active = null;
if ($user->relationLoaded('userLevel')) {
$user_level_active = $user->userLevel;
} else {
$user_level_active = $user->user_level; // Fallback auf Original-Relation
}
$this->user_level_active_pos = $user_level_active ? $user_level_active->pos : 0;
// Neues UserBusiness Objekt erstellen
$this->b_user = new UserBusiness();
// Account-Daten (mit Error-Handling)
$account = $user->relationLoaded('account') ? $user->account : null;
if (!$account) {
\Log::warning("BusinessUserItem: No account found for user {$user->id}");
}
$fill = [
'user_id' => $user->id,
'month' => $this->date->month,
'year' => $this->date->year,
'm_level_id' => $user->m_level,
'user_level_name' => $user_level_active ? $user_level_active->name : '',
'active_account' => $this->calculateActiveAccount($user),
'payment_account_date' => $user->payment_account ? $user->getPaymentAccountDateFormat(false) : null,
'active_date' => $user->active_date,
// Account-Daten mit Fallback
'm_account' => $account ? $account->m_account : '',
'email' => $user->email,
'first_name' => $account ? $account->first_name : '',
'last_name' => $account ? $account->last_name : '',
'user_birthday' => $account ? $account->birthday : null,
'user_phone' => $account ? $account->getPhoneNumber() : '',
// Sales Volume (mit Caching falls möglich)
'sales_volume_KP_points' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_KP_points'),
'sales_volume_TP_points' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_TP_points'),
'sales_volume_points_shop' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_shop'),
'sales_volume_points_KP_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_KP_sum'),
'sales_volume_points_TP_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_points_TP_sum'),
'sales_volume_total' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total'),
'sales_volume_total_shop' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total_shop'),
'sales_volume_total_sum' => $this->getUserSalesVolumeOptimized($user, 'sales_volume_total_sum'),
// Level-Daten mit Boundary-Checks
'margin' => $user_level_active ? max(0, $user_level_active->margin) : 0,
'margin_shop' => $user_level_active ? max(0, $user_level_active->margin_shop) : 0,
'qual_kp' => $user_level_active ? max(0, $user_level_active->qual_kp) : 0,
'qual_pp' => $user_level_active ? max(0, $user_level_active->qual_pp) : 0,
// Initialisierung
'payline_points' => 0,
'commission_pp_total' => 0,
'commission_shop_sales' => 0,
'commission_growth_total' => 0,
'version' => 2,
];
$this->b_user->fill($fill);
$this->b_user->business_lines = [];
$this->b_user->user_items = [];
// Shop-Provision berechnen (mit Boundary-Check)
$shopVolume = (float) $this->b_user->sales_volume_total_shop;
$shopMargin = (float) $this->b_user->margin_shop;
$this->b_user->commission_shop_sales = round($shopVolume / 100 * $shopMargin, 2);
\Log::debug("BusinessUserItem: Created optimized user {$user->id} for {$this->date->month}/{$this->date->year}");
}
/**
* Berechnet ob Account aktiv ist (mit Error-Handling)
*/
private function calculateActiveAccount(User $user): bool
{
try {
if (!$user->payment_account) {
return false;
}
return Carbon::parse($user->payment_account)->gt(Carbon::parse($this->date->start_date));
} catch (\Exception $e) {
\Log::warning("BusinessUserItem: Error calculating active account for user {$user->id}: " . $e->getMessage());
return false;
}
}
/**
* Optimierte Sales Volume Abfrage (mit potenziellem Caching)
*/
private function getUserSalesVolumeOptimized(User $user, string $field)
{
try {
// Hier könnte Caching implementiert werden
$cacheKey = "sales_volume_{$user->id}_{$this->date->month}_{$this->date->year}_{$field}";
// Für jetzt: Direkter Aufruf (später durch Cache ersetzen)
return $user->getUserSalesVolumeBy($this->date->month, $this->date->year, $field);
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error getting sales volume {$field} for user {$user->id}: " . $e->getMessage());
return 0; // Sicherer Fallback
}
}
// ===== ORIGINALE METHODEN (unverändert für Kompatibilität) =====
public function getSalesVolumeTotalMargin()
{
return $this->b_user->getSalesVolumeTotalMargin();
}
public function addUserID()
{
TreeCalcBotOptimized::addUserID($this->b_user->user_id);
}
public function getBUser()
{
return $this->b_user;
}
public function addBusinessLineToUser($line, $obj)
{
$this->b_user->business_lines[$line] = $obj;
}
public function addBusinessLinePoints($line, $points)
{
if (!isset($this->b_user->business_lines[$line])) {
\Log::warning("BusinessUserItem: Trying to add points to non-existent line {$line}");
return;
}
$obj = $this->b_user->business_lines[$line];
$obj->points += (float) $points; // Type-Safety
$this->b_user->business_lines[$line] = $obj;
}
public function addTotalTP($points)
{
$this->b_user->total_pp += (float) $points; // Type-Safety
}
public function isQualKP(): bool
{
return ($this->b_user->sales_volume_points_KP_sum >= $this->b_user->qual_kp);
}
public function isQualLevel(): bool
{
return !empty($this->b_user->qual_user_level);
}
public function isQualEqualLevel(): bool
{
if (!$this->b_user->qual_user_level) {
return false;
}
return ($this->b_user->m_level_id == $this->b_user->qual_user_level['id']);
}
public function getQualPaylines(): int
{
if (!$this->b_user->qual_user_level) {
return 0;
}
return (int) $this->b_user->qual_user_level['paylines'];
}
public function getRestQualKP(): float
{
$ret = $this->b_user->sales_volume_points_KP_sum - $this->b_user->qual_kp;
return max(0, $ret); // Boundary-Check
}
public function getCommissionTotal(): float
{
return round(
$this->b_user->commission_shop_sales +
$this->b_user->commission_pp_total +
$this->b_user->commission_growth_total,
2
);
}
// ===== PROVISIONSBERECHNUNG (Original-Logik) =====
public function calcQualPP(): void
{
try {
$qualUserLevel = $this->calcuQualLevel();
if ($qualUserLevel !== null) {
$this->setNextUserLevel();
$this->b_user->qual_user_level = $qualUserLevel->toArray();
$this->setQualNextLevel();
$this->calculateCommissions($qualUserLevel);
} else {
$this->setFirstQualLevel();
}
} catch (\Exception $e) {
\Log::error("BusinessUserItem: Error calculating qualifications for user {$this->b_user->user_id}: " . $e->getMessage());
}
}
/**
* Berechnet Provisionen mit Error-Handling
*/
private function calculateCommissions($qualUserLevel): void
{
$commission_pp_total = 0;
$commission_growth_total = 0;
// Payline-Provisionen
for ($i = 1; $i <= $qualUserLevel->paylines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$object = $this->b_user->business_lines[$i];
$margin = (float) $this->b_user->qual_user_level['pr_line_'.$i];
$points = (float) $object->points;
$object->margin = $margin;
$object->commission = round($points / 100 * $margin, 2);
$object->payline = true;
$commission_pp_total += $object->commission;
$this->b_user->business_lines[$i] = $object;
}
}
// Growth Bonus
if (!empty($qualUserLevel->growth_bonus)) {
$payline = (int) $this->b_user->qual_user_level['paylines'] + 1;
$maxlines = count($this->b_user->business_lines) + 1;
$growth_bonus = (float) $this->b_user->qual_user_level['growth_bonus'];
for ($i = $payline; $i <= $maxlines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$object = $this->b_user->business_lines[$i];
$points = (float) $object->points;
$object->margin = $growth_bonus;
$object->commission = round($points / 100 * $growth_bonus, 2);
$object->growth_bonus = true;
$commission_growth_total += $object->commission;
$this->b_user->business_lines[$i] = $object;
}
}
}
$this->b_user->commission_pp_total = $commission_pp_total;
$this->b_user->commission_growth_total = $commission_growth_total;
}
// ===== WEITERE ORIGINAL-METHODEN (gekürzt, vollständige Implementation in Original) =====
public function calcuQualLevel()
{
$qualUserLevels = UserLevel::where('qual_kp', '<=', $this->b_user->sales_volume_points_KP_sum)
->where('pos', '<=', $this->user_level_active_pos)
->orderBy('qual_pp', 'desc')
->get();
foreach ($qualUserLevels as $qualUserLevel) {
$payline_points = $this->getPointsforPayline($qualUserLevel->paylines);
$payline_points_qual_kp = $payline_points + $this->getRestQualKP();
if ($payline_points_qual_kp >= $qualUserLevel->qual_pp) {
$this->b_user->payline_points = $payline_points;
$this->b_user->payline_points_qual_kp = $payline_points_qual_kp;
return $qualUserLevel;
}
}
return null;
}
private function getPointsforPayline($paylines): float
{
$payline_points = 0;
for ($i = 1; $i <= $paylines; $i++) {
if (isset($this->b_user->business_lines[$i])) {
$payline_points += (float) $this->b_user->business_lines[$i]->points;
}
}
return $payline_points;
}
private function setQualNextLevel(): void
{
if (!$this->isQualEqualLevel()) {
$qualUserLevelNext = UserLevel::where('id', '=', $this->b_user->qual_user_level['next_id'])
->orderBy('qual_pp', 'asc')
->first();
if ($qualUserLevelNext) {
$this->b_user->qual_user_level_next = $qualUserLevelNext->toArray();
}
}
}
private function setNextUserLevel(): void
{
$nextQualUserLevel = UserLevel::where('qual_pp', '<=', $this->b_user->payline_points_qual_kp)
->where('pos', '>', $this->user_level_active_pos)
->orderBy('qual_pp', 'desc')
->first();
if ($nextQualUserLevel && $this->isQualKP()) {
$this->b_user->next_qual_user_level = $nextQualUserLevel->toArray();
} else {
$nextCanUserLevel = UserLevel::where('pos', '>', $this->user_level_active_pos)
->orderBy('qual_pp', 'asc')
->first();
if ($nextCanUserLevel) {
$this->b_user->next_can_user_level = $nextCanUserLevel->toArray();
}
}
}
private function setFirstQualLevel(): void
{
$qualUserLevelNext = UserLevel::where('pos', '=', 1)
->orderBy('qual_pp', 'asc')
->first();
if ($qualUserLevelNext) {
$this->b_user->qual_user_level_next = $qualUserLevelNext->toArray();
}
}
// Magic Methods für Property-Zugriff (Rückwärtskompatibilität)
public function __get($name)
{
if (isset($this->b_user->$name)) {
return $this->b_user->$name;
}
// Legacy-Properties
$legacyMap = [
'sales_volume_points_KP_sum' => 'sales_volume_points_KP_sum',
'sales_volume_points_TP_sum' => 'sales_volume_points_TP_sum',
'business_lines' => 'business_lines',
'user_id' => 'user_id'
];
if (isset($legacyMap[$name]) && isset($this->b_user->{$legacyMap[$name]})) {
return $this->b_user->{$legacyMap[$name]};
}
return null;
}
// Weitere Original-Methoden (checkSponsor, readParentsBusinessUsers, etc.)
// Diese bleiben unverändert für vollständige Kompatibilität
public function checkSponsor($user) { /* Original-Implementation */ }
public function readParentsBusinessUsers() { /* Original-Implementation */ }
public function readStoredParentsBusinessUsers($userBusinessStructure) { /* Original-Implementation */ }
public function isSave(): bool { return $this->b_user && $this->b_user->exists; }
}