argument('incentive_id')) { $incentive = Incentive::find($id); if (! $incentive) { $this->error("Incentive #{$id} nicht gefunden."); return self::FAILURE; } return $this->processIncentive($incentive, $repairService); } $incentives = Incentive::active()->get(); if ($incentives->isEmpty()) { $this->info('Keine aktiven Incentives gefunden.'); return self::SUCCESS; } $exitCode = self::SUCCESS; foreach ($incentives as $incentive) { if ($this->processIncentive($incentive, $repairService) !== self::SUCCESS) { $exitCode = self::FAILURE; } } return $exitCode; } private function processIncentive(Incentive $incentive, IncentivePointsLogRepairService $repairService): int { $force = $this->option('force'); $skipRepair = $this->option('skip-repair'); $verbose = $this->option('verbose-details'); $this->info("=== {$incentive->name} (ID: {$incentive->id}) ==="); $this->info(" Zeitraum: {$incentive->qualification_start->format('d.m.Y')} - {$incentive->qualification_end->format('d.m.Y')}"); if ($force) { $this->info(' Modus: FORCE (Tracking + Log aus Quelldaten neu aufbauen)'); } elseif ($skipRepair) { $this->info(' Modus: Nur Neuberechnung (Summen/Ranking aus bestehendem Log)'); } else { $this->info(' Modus: Tracking nachziehen + FK-Reparatur + SV-Logs + Neuberechnung'); } $stubAdded = IncentiveParticipant::ensureConsultantsForIncentive($incentive); if ($stubAdded > 0) { $this->info(" Berater-Teilnehmer neu angelegt (ohne Zustimmung): {$stubAdded}"); } $participants = $incentive->participants()->with('user', 'user.account')->get(); $this->info(" Teilnehmer: {$participants->count()}"); $this->newLine(); $stats = [ 'processed' => 0, 'errors' => 0, 'with_points' => 0, 'with_partners' => 0, 'with_abos' => 0, 'tracking_partner_added' => 0, 'tracking_abo_added' => 0, 'repair_partner_fk' => 0, 'repair_abo_fk' => 0, 'repair_onetime_partner_fk' => 0, 'repair_onetime_abo_fk' => 0, 'sv_logs_added' => 0, ]; $errors = []; $bar = $this->output->createProgressBar($participants->count()); $bar->start(); foreach ($participants as $participant) { try { if (! $participant->user) { $bar->advance(); continue; } if ($force) { $participant->rebuildFromSourceTables()->save(); } else { if (! $skipRepair) { $stats['tracking_partner_added'] += $repairService->syncMissingTrackingPartners($participant); $stats['tracking_abo_added'] += $repairService->syncMissingTrackingAbos($participant); $r = $repairService->repairForeignKeys($participant); $stats['repair_partner_fk'] += $r['partner_fk']; $stats['repair_abo_fk'] += $r['abo_fk']; $stats['repair_onetime_partner_fk'] += $r['onetime_partner_fk']; $stats['repair_onetime_abo_fk'] += $r['onetime_abo_fk']; $stats['sv_logs_added'] += $repairService->syncMissingSalesVolumeLogs($participant); } $participant->recalculateFromTrackingTables()->save(); } $stats['processed']++; if ($participant->total_points > 0) { $stats['with_points']++; } if ($participant->qualified_partners > 0) { $stats['with_partners']++; } if ($participant->qualified_abos > 0) { $stats['with_abos']++; } if ($verbose && $participant->total_points > 0) { $name = $participant->user->account ? $participant->user->account->first_name.' '.$participant->user->account->last_name : ($participant->user->email ?? 'User #'.$participant->user_id); $bar->clear(); $this->line(" {$name}: {$participant->total_points} Pkt, {$participant->qualified_partners} Partner, {$participant->qualified_abos} Abos"); $bar->display(); } } catch (\Throwable $e) { $stats['errors']++; $errors[] = "Participant #{$participant->id} (User #{$participant->user_id}): {$e->getMessage()}"; Log::error('IncentiveCalculation error for participant '.$participant->id.': '.$e->getMessage()); } $bar->advance(); } $bar->finish(); $this->newLine(2); IncentiveTracker::updateRanking($incentive); $ranked = IncentiveParticipant::where('incentive_id', $incentive->id) ->whereNotNull('rank') ->count(); $tableRows = [ ['Verarbeitet', (string) $stats['processed']], ['Fehler', (string) $stats['errors']], ['Mit Punkten', (string) $stats['with_points']], ['Mit Partnern', (string) $stats['with_partners']], ['Mit Abos', (string) $stats['with_abos']], ['Im Ranking', (string) $ranked], ]; if (! $force) { $tableRows[] = ['Neupartner-Trackings nachgezogen', (string) $stats['tracking_partner_added']]; $tableRows[] = ['Neuabo-Trackings nachgezogen', (string) $stats['tracking_abo_added']]; $tableRows[] = ['FK Partner (akkum.) repariert', (string) $stats['repair_partner_fk']]; $tableRows[] = ['FK Abo (akkum.) repariert', (string) $stats['repair_abo_fk']]; $tableRows[] = ['FK Partner (Einmal) repariert', (string) $stats['repair_onetime_partner_fk']]; $tableRows[] = ['FK Abo (Einmal) repariert', (string) $stats['repair_onetime_abo_fk']]; $tableRows[] = ['Neue SV-Log-Eintraege', (string) $stats['sv_logs_added']]; } $this->table(['Metrik', 'Wert'], $tableRows); if (! empty($errors)) { $this->error('Fehler:'); foreach ($errors as $err) { $this->line(" - {$err}"); } return self::FAILURE; } $this->info('Fertig.'); return self::SUCCESS; } }