| <?php
|
|
|
| namespace App\Services;
|
|
|
| use App\Models\AnalyticsSnapshot;
|
| use App\Models\RevenueTracking;
|
| use App\Models\Order;
|
| use App\Models\User;
|
| use App\Models\Product;
|
| use Carbon\Carbon;
|
| use Illuminate\Support\Facades\DB;
|
|
|
| class AnalyticsService
|
| {
|
| |
| |
|
|
| public function getTodayRevenue(): array
|
| {
|
| $today = Carbon::today();
|
| $yesterday = Carbon::yesterday();
|
|
|
|
|
| $todayRevenue = Order::whereDate('created_at', $today)
|
| ->where('status', '!=', 'cancelled')
|
| ->sum('total_amount');
|
|
|
| $yesterdayRevenue = Order::whereDate('created_at', $yesterday)
|
| ->where('status', '!=', 'cancelled')
|
| ->sum('total_amount');
|
|
|
| $percentageChange = $yesterdayRevenue > 0
|
| ? (($todayRevenue - $yesterdayRevenue) / $yesterdayRevenue) * 100
|
| : ($todayRevenue > 0 ? 100 : 0);
|
|
|
| return [
|
| 'current' => (float) $todayRevenue,
|
| 'previous' => (float) $yesterdayRevenue,
|
| 'percentage_change' => round($percentageChange, 2),
|
| 'trend' => $percentageChange >= 0 ? 'up' : 'down'
|
| ];
|
| }
|
|
|
| |
| |
|
|
| public function getProfitData(string $period = 'today'): array
|
| {
|
| $query = Order::where('status', '!=', 'cancelled');
|
| $previousQuery = Order::where('status', '!=', 'cancelled');
|
|
|
| switch ($period) {
|
| case 'today':
|
| $query->whereDate('created_at', Carbon::today());
|
| $previousQuery->whereDate('created_at', Carbon::yesterday());
|
| break;
|
| case 'week':
|
| $query->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]);
|
| $previousQuery->whereBetween('created_at', [
|
| Carbon::now()->subWeek()->startOfWeek(),
|
| Carbon::now()->subWeek()->endOfWeek()
|
| ]);
|
| break;
|
| case 'month':
|
| $query->whereMonth('created_at', Carbon::now()->month);
|
| $previousQuery->whereMonth('created_at', Carbon::now()->subMonth()->month);
|
| break;
|
| }
|
|
|
| $revenue = $query->sum('total_amount');
|
| $previousRevenue = $previousQuery->sum('total_amount');
|
|
|
|
|
| $estimatedProfitMargin = 0.30;
|
| $profit = $revenue * $estimatedProfitMargin;
|
| $previousProfit = $previousRevenue * $estimatedProfitMargin;
|
| $cost = $revenue - $profit;
|
|
|
| $profitMarginPercentage = $revenue > 0 ? ($profit / $revenue) * 100 : 0;
|
| $percentageChange = $previousProfit > 0
|
| ? (($profit - $previousProfit) / $previousProfit) * 100
|
| : ($profit > 0 ? 100 : 0);
|
|
|
| return [
|
| 'profit' => (float) $profit,
|
| 'revenue' => (float) $revenue,
|
| 'cost' => (float) $cost,
|
| 'profit_margin' => round($profitMarginPercentage, 2),
|
| 'percentage_change' => round($percentageChange, 2),
|
| 'trend' => $percentageChange >= 0 ? 'up' : 'down'
|
| ];
|
| }
|
|
|
| |
| |
|
|
| public function getAggregatedData(string $period, Carbon $date = null): array
|
| {
|
| $date = $date ?? Carbon::now();
|
|
|
| switch ($period) {
|
| case 'daily':
|
| return $this->getDailyHourlyData($date);
|
| case 'weekly':
|
| return $this->getWeeklyDailyData($date);
|
| case 'monthly':
|
| return $this->getMonthlyDailyData($date);
|
| default:
|
| return [];
|
| }
|
| }
|
|
|
| |
| |
|
|
| private function getDailyHourlyData(Carbon $date): array
|
| {
|
| $hourlyData = [];
|
|
|
| for ($hour = 0; $hour < 24; $hour++) {
|
| $startTime = $date->copy()->hour($hour)->minute(0)->second(0);
|
| $endTime = $startTime->copy()->addHour();
|
|
|
| $orders = Order::where('status', '!=', 'cancelled')
|
| ->whereBetween('created_at', [$startTime, $endTime])
|
| ->get();
|
|
|
| $revenue = $orders->sum('total_amount');
|
| $profit = $revenue * 0.30;
|
| $ordersCount = $orders->count();
|
| $customersCount = $orders->pluck('customer_name')->unique()->count();
|
|
|
| $hourlyData[] = [
|
| 'hour' => $hour,
|
| 'revenue' => (float) $revenue,
|
| 'profit' => (float) $profit,
|
| 'orders' => $ordersCount,
|
| 'customers' => $customersCount,
|
| ];
|
| }
|
|
|
| return $hourlyData;
|
| }
|
|
|
| |
| |
|
|
| private function getWeeklyDailyData(Carbon $date): array
|
| {
|
| $startOfWeek = $date->copy()->startOfWeek();
|
| $dailyData = [];
|
|
|
| for ($day = 0; $day < 7; $day++) {
|
| $currentDate = $startOfWeek->copy()->addDays($day);
|
|
|
| $orders = Order::where('status', '!=', 'cancelled')
|
| ->whereDate('created_at', $currentDate)
|
| ->get();
|
|
|
| $revenue = $orders->sum('total_amount');
|
| $profit = $revenue * 0.30;
|
| $ordersCount = $orders->count();
|
| $customersCount = $orders->pluck('customer_name')->unique()->count();
|
|
|
| $dailyData[] = [
|
| 'date' => $currentDate->format('Y-m-d'),
|
| 'day_name' => $currentDate->format('l'),
|
| 'revenue' => (float) $revenue,
|
| 'profit' => (float) $profit,
|
| 'orders' => $ordersCount,
|
| 'customers' => $customersCount,
|
| ];
|
| }
|
|
|
| return $dailyData;
|
| }
|
|
|
| |
| |
|
|
| private function getMonthlyDailyData(Carbon $date): array
|
| {
|
| $daysInMonth = $date->daysInMonth;
|
| $dailyData = [];
|
|
|
| for ($day = 1; $day <= $daysInMonth; $day++) {
|
| $currentDate = $date->copy()->day($day);
|
|
|
| $orders = Order::where('status', '!=', 'cancelled')
|
| ->whereDate('created_at', $currentDate)
|
| ->get();
|
|
|
| $revenue = $orders->sum('total_amount');
|
| $profit = $revenue * 0.30;
|
| $ordersCount = $orders->count();
|
| $customersCount = $orders->pluck('customer_name')->unique()->count();
|
|
|
| $dailyData[] = [
|
| 'date' => $currentDate->format('Y-m-d'),
|
| 'day' => $day,
|
| 'revenue' => (float) $revenue,
|
| 'profit' => (float) $profit,
|
| 'orders' => $ordersCount,
|
| 'customers' => $customersCount,
|
| ];
|
| }
|
|
|
| return $dailyData;
|
| }
|
|
|
| |
| |
|
|
| public function updateAnalyticsSnapshots(): void
|
| {
|
| $this->updateHourlySnapshots();
|
| $this->updateDailySnapshots();
|
| }
|
|
|
| |
| |
|
|
| private function updateHourlySnapshots(): void
|
| {
|
| $today = Carbon::today();
|
| $currentHour = Carbon::now()->hour;
|
|
|
| for ($hour = 0; $hour <= $currentHour; $hour++) {
|
| $startTime = $today->copy()->hour($hour);
|
| $endTime = $startTime->copy()->addHour();
|
|
|
| $revenue = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('amount');
|
| $cost = RevenueTracking::whereBetween('created_at', [$startTime, $endTime])->sum('cost');
|
| $profit = $revenue - $cost;
|
| $ordersCount = Order::whereBetween('created_at', [$startTime, $endTime])->count();
|
| $customersCount = Order::whereBetween('created_at', [$startTime, $endTime])
|
| ->distinct('user_id')->count('user_id');
|
|
|
| AnalyticsSnapshot::updateOrCreate(
|
| ['date' => $today->format('Y-m-d'), 'hour' => $hour],
|
| [
|
| 'revenue' => $revenue,
|
| 'profit' => $profit,
|
| 'orders_count' => $ordersCount,
|
| 'customers_count' => $customersCount,
|
| 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0,
|
| 'conversion_rate' => 0 // Will be calculated separately
|
| ]
|
| );
|
| }
|
| }
|
|
|
| |
| |
|
|
| private function updateDailySnapshots(): void
|
| {
|
| $yesterday = Carbon::yesterday();
|
|
|
| $revenue = RevenueTracking::whereDate('created_at', $yesterday)->sum('amount');
|
| $cost = RevenueTracking::whereDate('created_at', $yesterday)->sum('cost');
|
| $profit = $revenue - $cost;
|
| $ordersCount = Order::whereDate('created_at', $yesterday)->count();
|
| $customersCount = Order::whereDate('created_at', $yesterday)
|
| ->distinct('user_id')->count('user_id');
|
|
|
| AnalyticsSnapshot::updateOrCreate(
|
| ['date' => $yesterday->format('Y-m-d'), 'hour' => null],
|
| [
|
| 'revenue' => $revenue,
|
| 'profit' => $profit,
|
| 'orders_count' => $ordersCount,
|
| 'customers_count' => $customersCount,
|
| 'avg_order_value' => $ordersCount > 0 ? $revenue / $ordersCount : 0,
|
| 'conversion_rate' => 0 // Will be calculated separately
|
| ]
|
| );
|
| }
|
|
|
| |
| |
|
|
| public function getTopProducts(int $limit = 10): array
|
| {
|
|
|
| $products = Product::select('id', 'name', 'price', 'Amount')
|
| ->get()
|
| ->map(function ($product) {
|
|
|
|
|
| $estimatedSold = max(0, 100 - $product->Amount);
|
| $totalRevenue = $estimatedSold * $product->price;
|
|
|
| return [
|
| 'product_name' => $product->name,
|
| 'total_quantity' => $estimatedSold,
|
| 'total_revenue' => (float) $totalRevenue,
|
| 'current_stock' => $product->Amount,
|
| 'price' => (float) $product->price
|
| ];
|
| })
|
| ->sortByDesc('total_revenue')
|
| ->take($limit)
|
| ->values()
|
| ->toArray();
|
|
|
| return $products;
|
| }
|
|
|
| |
| |
|
|
| public function getCustomerAnalytics(): array
|
| {
|
| $totalCustomers = User::count();
|
| $newCustomersToday = User::whereDate('created_at', Carbon::today())->count();
|
|
|
|
|
| $customersWithOrdersToday = Order::whereDate('created_at', Carbon::today())
|
| ->distinct('customer_name')
|
| ->count('customer_name');
|
|
|
|
|
| $ordersToday = Order::whereDate('created_at', Carbon::today())->count();
|
|
|
| return [
|
| 'total_customers' => $totalCustomers,
|
| 'new_today' => $newCustomersToday,
|
| 'active_today' => $customersWithOrdersToday,
|
| 'orders_today' => $ordersToday,
|
| 'new_percentage' => $totalCustomers > 0 ? ($newCustomersToday / $totalCustomers) * 100 : 0,
|
| 'conversion_rate' => $customersWithOrdersToday > 0 ? ($ordersToday / $customersWithOrdersToday) * 100 : 0
|
| ];
|
| }
|
|
|
| |
| |
|
|
| public function getOrderStats(): array
|
| {
|
| $today = Carbon::today();
|
| $yesterday = Carbon::yesterday();
|
|
|
| $todayOrders = Order::whereDate('created_at', $today)->count();
|
| $yesterdayOrders = Order::whereDate('created_at', $yesterday)->count();
|
|
|
| $percentageChange = $yesterdayOrders > 0
|
| ? (($todayOrders - $yesterdayOrders) / $yesterdayOrders) * 100
|
| : ($todayOrders > 0 ? 100 : 0);
|
|
|
| return [
|
| 'current' => $todayOrders,
|
| 'previous' => $yesterdayOrders,
|
| 'percentage_change' => round($percentageChange, 2),
|
| 'trend' => $percentageChange >= 0 ? 'up' : 'down',
|
| 'total_orders' => Order::count(),
|
| 'pending_orders' => Order::where('status', 'pending')->count(),
|
| 'completed_orders' => Order::where('status', 'completed')->count()
|
| ];
|
| }
|
|
|
| |
| |
|
|
| public function getProductStats(): array
|
| {
|
| $totalProducts = Product::count();
|
| $lowStockProducts = Product::where('Amount', '<', 10)->count();
|
| $outOfStockProducts = Product::where('Amount', 0)->count();
|
|
|
| return [
|
| 'total_products' => $totalProducts,
|
| 'low_stock' => $lowStockProducts,
|
| 'out_of_stock' => $outOfStockProducts,
|
| 'in_stock' => $totalProducts - $outOfStockProducts,
|
| 'average_price' => (float) Product::avg('price')
|
| ];
|
| }
|
| } |