>, summary: array} */ public function details(User $viewer, int $line, string $metric, int $month, int $year): array { $metricLabels = $this->dashboardService->metricLabels(); if (! array_key_exists($metric, $metricLabels)) { abort(404); } $userIds = $this->dashboardService->lineUserIds($viewer->id, $line); $rows = match ($metric) { 'consultants' => $this->consultantRows($userIds), 'new_partners' => $this->newPartnerRows($userIds, $month, $year), 'team_partner_abos' => $this->partnerAboRows($userIds, $month, $year), 'team_customer_abos' => $this->customerAboRows($userIds, $month, $year), 'own_points', 'external_points', 'customer_abo_points', 'customer_single_order_points', 'customer_other_points', 'total_points', 'shop_1000' => $this->pointsRows($userIds, $month, $year, $metric), default => [], }; return [ 'metric' => $metric, 'metric_label' => $metricLabels[$metric], 'line' => $line, 'line_label' => $line > 0 ? 'Linie '.$line : 'Alle Linien', 'month' => $month, 'year' => $year, 'rows' => $rows, 'summary' => $this->summary($rows), ]; } /** * @param array> $rows * @return array{count: int, points: float, own_points: float, external_points: float, customer_abo_points: float, customer_single_order_points: float, customer_other_points: float, total_points: float, deliveries: int} */ private function summary(array $rows): array { return [ 'count' => count($rows), 'points' => (float) collect($rows)->sum('points'), 'own_points' => (float) collect($rows)->sum('own_points'), 'external_points' => (float) collect($rows)->sum('external_points'), 'customer_abo_points' => (float) collect($rows)->sum('customer_abo_points'), 'customer_single_order_points' => (float) collect($rows)->sum('customer_single_order_points'), 'customer_other_points' => (float) collect($rows)->sum('customer_other_points'), 'total_points' => (float) collect($rows)->sum('total_points'), 'deliveries' => (int) collect($rows)->sum('deliveries'), ]; } /** * @param int[] $userIds * @return array> */ private function consultantRows(array $userIds): array { return User::query() ->with(['account', 'user_level']) ->whereIn('id', $userIds) ->whereNotNull('m_level') ->whereNotNull('payment_account') ->orderBy('id') ->get() ->map(fn (User $user) => [ 'type' => 'user', 'user_id' => $user->id, 'name' => $this->userName($user), 'email' => $user->email, 'career_level' => $this->careerLevel($user), 'is_account_active' => Carbon::parse($user->payment_account)->isFuture(), 'account_status' => Carbon::parse($user->payment_account)->isFuture() ? 'Aktiv' : 'Abgelaufen', 'active_date' => $this->formatDate($user->active_date), 'payment_account' => $this->formatDate($user->payment_account), ]) ->values() ->all(); } /** * @param int[] $userIds * @return array> */ private function newPartnerRows(array $userIds, int $month, int $year): array { $startDate = Carbon::create($year, $month, 1)->startOfMonth(); $endDate = Carbon::create($year, $month, 1)->endOfMonth(); return User::query() ->with(['account', 'user_level']) ->whereIn('id', $userIds) ->whereNotNull('m_level') ->whereNotNull('payment_account') ->whereBetween('active_date', [$startDate, $endDate]) ->orderBy('active_date') ->get() ->filter(fn (User $user) => Carbon::parse($user->payment_account)->isFuture()) ->map(fn (User $user) => [ 'type' => 'user', 'user_id' => $user->id, 'name' => $this->userName($user), 'email' => $user->email, 'career_level' => $this->careerLevel($user), 'active_date' => $this->formatDate($user->active_date), 'payment_account' => $this->formatDate($user->payment_account), ]) ->values() ->all(); } /** * @param int[] $userIds * @return array> */ private function partnerAboRows(array $userIds, int $month, int $year): array { return $this->activeAboQuery() ->with(['user.account', 'user.user_level', 'user_abo_items.product', 'user_abo_orders.shopping_order.shopping_payments.payment_transactions']) ->whereIn('user_id', $userIds) ->where('is_for', 'me') ->orderBy('next_date') ->get() ->map(fn (UserAbo $abo) => [ 'type' => 'abo', 'abo_id' => $abo->id, 'user_id' => $abo->user_id, 'name' => $abo->user ? $this->userName($abo->user) : '#'.$abo->user_id, 'email' => $abo->user?->email, 'career_level' => $abo->user ? $this->careerLevel($abo->user) : '-', 'points' => $abo->getTotalPoints(), 'start_date' => $this->formatDate($abo->getRawOriginal('start_date')), 'is_new_this_month' => $this->isAboNewInMonth($abo, $month, $year), 'next_date' => $this->formatDate($abo->next_date), 'deliveries' => $abo->getCountOrders(), 'status' => $abo->status, 'status_label' => $abo->getStatusType(), 'status_badge' => $abo->getStatusFormated(), 'status_reason' => $this->aboStatusReason($abo), ]) ->values() ->all(); } /** * @param int[] $userIds * @return array> */ private function customerAboRows(array $userIds, int $month, int $year): array { return $this->activeAboQuery() ->with(['member.account', 'member.user_level', 'user.account', 'user_abo_items.product', 'user_abo_orders.shopping_order.shopping_payments.payment_transactions']) ->whereIn('member_id', $userIds) ->where('is_for', 'ot') ->orderBy('member_id') ->orderBy('next_date') ->get() ->map(fn (UserAbo $abo) => [ 'type' => 'customer_abo', 'abo_id' => $abo->id, 'user_id' => $abo->user_id, 'member_id' => $abo->member_id, 'name' => $abo->user ? $this->userName($abo->user) : ($abo->email ?: '#'.$abo->user_id), 'email' => $abo->email ?: $abo->user?->email, 'consultant_name' => $abo->member ? $this->userName($abo->member) : '#'.$abo->member_id, 'career_level' => $abo->member ? $this->careerLevel($abo->member) : '-', 'points' => $abo->getTotalPoints(), 'start_date' => $this->formatDate($abo->getRawOriginal('start_date')), 'is_new_this_month' => $this->isAboNewInMonth($abo, $month, $year), 'next_date' => $this->formatDate($abo->next_date), 'deliveries' => $abo->getCountOrders(), 'status' => $abo->status, 'status_label' => $abo->getStatusType(), 'status_badge' => $abo->getStatusFormated(), 'status_reason' => $this->aboStatusReason($abo), ]) ->values() ->all(); } /** * @param int[] $userIds * @return array> */ private function pointsRows(array $userIds, int $month, int $year, string $metric): array { $rows = UserSalesVolume::query() ->leftJoin('shopping_orders', 'shopping_orders.id', '=', 'user_sales_volumes.shopping_order_id') ->whereIn('user_id', $userIds) ->where('month', $month) ->where('year', $year) ->select('user_sales_volumes.user_id') ->selectRaw('COALESCE(SUM(month_KP_points), 0) as own_points') ->selectRaw('COALESCE(SUM(month_shop_points), 0) as external_points') ->selectRaw('COALESCE(SUM(CASE WHEN shopping_orders.is_abo = 1 THEN month_shop_points ELSE 0 END), 0) as customer_abo_points') ->selectRaw('COALESCE(SUM(CASE WHEN shopping_orders.id IS NOT NULL AND (shopping_orders.is_abo = 0 OR shopping_orders.is_abo IS NULL) THEN month_shop_points ELSE 0 END), 0) as customer_single_order_points') ->selectRaw('COALESCE(SUM(CASE WHEN shopping_orders.id IS NULL THEN month_shop_points ELSE 0 END), 0) as customer_other_points') ->selectRaw('COALESCE(SUM(month_KP_points), 0) + COALESCE(SUM(month_shop_points), 0) as total_points') ->groupBy('user_sales_volumes.user_id'); if ($metric === 'own_points') { $rows->havingRaw('COALESCE(SUM(month_KP_points), 0) > 0'); } if ($metric === 'external_points') { $rows->havingRaw('COALESCE(SUM(month_shop_points), 0) > 0'); } if ($metric === 'customer_abo_points') { $rows->havingRaw('COALESCE(SUM(CASE WHEN shopping_orders.is_abo = 1 THEN month_shop_points ELSE 0 END), 0) > 0'); } if ($metric === 'customer_single_order_points') { $rows->havingRaw('COALESCE(SUM(CASE WHEN shopping_orders.id IS NOT NULL AND (shopping_orders.is_abo = 0 OR shopping_orders.is_abo IS NULL) THEN month_shop_points ELSE 0 END), 0) > 0'); } if ($metric === 'customer_other_points') { $rows->havingRaw('COALESCE(SUM(CASE WHEN shopping_orders.id IS NULL THEN month_shop_points ELSE 0 END), 0) > 0'); } if ($metric === 'total_points') { $rows->havingRaw('COALESCE(SUM(month_KP_points), 0) + COALESCE(SUM(month_shop_points), 0) > 0'); } if ($metric === 'shop_1000') { $rows->havingRaw('COALESCE(SUM(month_KP_points), 0) + COALESCE(SUM(month_shop_points), 0) >= 1000'); } $salesRows = $rows->orderByDesc('total_points')->get(); $users = User::query()->with(['account', 'user_level'])->whereIn('id', $salesRows->pluck('user_id'))->get()->keyBy('id'); return $salesRows ->map(fn (UserSalesVolume $row) => [ 'type' => 'points', 'user_id' => $row->user_id, 'name' => $users->has($row->user_id) ? $this->userName($users->get($row->user_id)) : '#'.$row->user_id, 'email' => $users->get($row->user_id)?->email, 'career_level' => $users->has($row->user_id) ? $this->careerLevel($users->get($row->user_id)) : '-', 'own_points' => (float) $row->own_points, 'external_points' => (float) $row->external_points, 'customer_abo_points' => (float) $row->customer_abo_points, 'customer_single_order_points' => (float) $row->customer_single_order_points, 'customer_other_points' => (float) $row->customer_other_points, 'total_points' => (float) $row->total_points, ]) ->values() ->all(); } private function activeAboQuery(): Builder { return UserAbo::query() ->where('active', true) ->whereNotIn('status', [4, 5, 6]); } private function userName(User $user): string { $name = trim(($user->account?->first_name ?? '').' '.($user->account?->last_name ?? '')); return $name !== '' ? $name : ($user->email ?: '#'.$user->id); } private function careerLevel(User $user): string { return $user->user_level?->name ?: ($user->m_level ? 'Level '.$user->m_level : '-'); } private function aboStatusReason(UserAbo $abo): ?string { if ((int) $abo->status === 2) { return null; } $transaction = $abo->user_abo_orders ->sortByDesc('created_at') ->pluck('shopping_order') ->filter() ->map(fn ($order) => $order->getLastShoppingPaymentTransaction()) ->filter() ->first(); if (! $transaction) { return null; } $message = $transaction->errormessage ?: $transaction->customermessage; if (! $message) { return null; } return $transaction->errorcode ? '['.$transaction->errorcode.'] '.$message : $message; } private function isAboNewInMonth(UserAbo $abo, int $month, int $year): bool { $startDate = $abo->getRawOriginal('start_date'); if (! $startDate) { return false; } $date = Carbon::parse($startDate); return (int) $date->month === $month && (int) $date->year === $year; } private function formatDate(mixed $date): ?string { if ($date === null || $date === '') { return null; } return Carbon::parse($date)->format('d.m.Y'); } }