$this->getStats($today, $yesterday, fn ($date) => Order::whereDate('created_at', $date)->count()), 'total_revenue' => $this->getStats($today, $yesterday, fn ($date) => Order::whereDate('created_at', $date)->sum('total')), 'total_expenses' => $this->getStats($today, $yesterday, fn ($date) => Expense::whereDate('created_at', $date)->sum('amount')), 'cogs' => $this->getStats($today, $yesterday, fn ($date) => Order::whereDate('created_at', $date)->sum('cogs')), 'total_discount' => $this->getStats($today, $yesterday, fn ($date) => Order::whereDate('created_at', $date)->sum('discount')), 'total_purchases' => $this->getStats($today, $yesterday, fn ($date) => Purchase::whereDate('created_at', $date)->sum('total')), 'products_sold' => $this->getStats($today, $yesterday, fn ($date) => OrderItem::whereHas('order', fn ($q) => $q->whereDate('created_at', $date))->sum('qty')), 'total_customers' => $this->getStats($today, $yesterday, fn ($date) => Order::whereDate('created_at', $date)->distinct('customer_name')->count('customer_name')), ]; $stats['aov'] = $this->calculateAov($stats['total_revenue'], $stats['total_sales']); $stats['gross_profit'] = $this->calculateDiff($stats['total_revenue'], $stats['cogs']); $stats['net_profit'] = $this->calculateDiff($stats['gross_profit'], $stats['total_expenses']); $stats['profit_margin'] = $this->calculateMargin($stats['net_profit'], $stats['total_revenue']); $isSqlite = DB::getDriverName() === 'sqlite'; $hourSelect = $isSqlite ? "CAST(strftime('%H', created_at) AS INTEGER)" : 'HOUR(created_at)'; // Get sales by hour $todayOrders = DB::table('orders') ->whereDate('created_at', $today) ->select( DB::raw("$hourSelect as hour"), DB::raw('COUNT(*) as total') ) ->groupBy(DB::raw($hourSelect)) ->get(); $yesterdayOrders = DB::table('orders') ->whereDate('created_at', $yesterday) ->select( DB::raw("$hourSelect as hour"), DB::raw('COUNT(*) as total') ) ->groupBy(DB::raw($hourSelect)) ->get(); $salesByHour = collect(range(0, 23))->map(function ($hour) use ($todayOrders, $yesterdayOrders) { $todayFound = $todayOrders->firstWhere('hour', $hour); $yesterdayFound = $yesterdayOrders->firstWhere('hour', $hour); return [ 'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT).':00', 'today' => $todayFound ? (int) $todayFound->total : 0, 'yesterday' => $yesterdayFound ? (int) $yesterdayFound->total : 0, ]; }); $paymentMethods = DB::table('orders') ->select('payment_method as name', DB::raw('COUNT(*) as total')) ->whereDate('created_at', $today) ->groupBy('payment_method') ->get() ->map(function ($item) { if ($item->name) { $item->name = PaymentMethod::tryFrom($item->name)?->label() ?? $item->name; } return $item; }); $orderStatuses = DB::table('orders') ->select('order_status as name', DB::raw('COUNT(*) as total')) ->whereDate('created_at', $today) ->groupBy('order_status') ->get() ->map(function ($item) { if ($item->name) { $item->name = OrderStatus::tryFrom($item->name)?->label() ?? $item->name; } return $item; }); $orderChannels = DB::table('orders') ->select('order_channel as name', DB::raw('COUNT(*) as total')) ->whereDate('created_at', $today) ->groupBy('order_channel') ->get() ->map(function ($item) { if ($item->name) { $item->name = OrderChannel::tryFrom($item->name)?->label() ?? $item->name; } return $item; }); $topProducts = DB::table('order_items') ->join('products', 'order_items.product_id', '=', 'products.id') ->join('orders', 'order_items.order_id', '=', 'orders.id') ->select('products.name as name', DB::raw('SUM(order_items.qty) as total')) ->whereDate('orders.created_at', $today) ->groupBy('products.name') ->orderByDesc('total') ->limit(5) ->get(); $topCustomers = DB::table('orders') ->select('customer_name as name', DB::raw('SUM(total) as total')) ->whereDate('created_at', $today) ->whereNotNull('customer_name') ->groupBy('customer_name') ->orderByDesc('total') ->limit(5) ->get(); return Inertia::render('dashboard', [ 'stats' => $stats, 'salesByHour' => $salesByHour, 'paymentMethods' => $paymentMethods, 'orderStatuses' => $orderStatuses, 'orderChannels' => $orderChannels, 'topProducts' => $topProducts, 'topCustomers' => $topCustomers, ]); } private function getStats($today, $yesterday, $callback): array { $todayVal = $callback($today); $yesterdayVal = $callback($yesterday); $diff = $todayVal - $yesterdayVal; $change = $this->calculatePercentageChange($todayVal, $yesterdayVal); return [ 'value' => $todayVal, 'yesterday' => $yesterdayVal, 'diff' => $diff, 'change' => $change, ]; } private function calculatePercentageChange($current, $previous): ?string { if ($previous == 0) { return $current == 0 ? 0 : null; } return round((($current - $previous) / abs($previous)) * 100, 2); } private function calculateAov($revenue, $sales) { $todayVal = $sales['value'] > 0 ? $revenue['value'] / $sales['value'] : 0; $yesterdayVal = $sales['yesterday'] > 0 ? $revenue['yesterday'] / $sales['yesterday'] : 0; return [ 'value' => $todayVal, 'yesterday' => $yesterdayVal, 'change' => $this->calculatePercentageChange($todayVal, $yesterdayVal), ]; } private function calculateDiff($a, $b): array { $todayVal = $a['value'] - $b['value']; $yesterdayVal = $a['yesterday'] - $b['yesterday']; return [ 'value' => $todayVal, 'yesterday' => $yesterdayVal, 'change' => $this->calculatePercentageChange($todayVal, $yesterdayVal), ]; } private function calculateMargin($profit, $revenue): array { $todayVal = $revenue['value'] > 0 ? ($profit['value'] / $revenue['value']) * 100 : 0; $yesterdayVal = $revenue['yesterday'] > 0 ? ($profit['yesterday'] / $revenue['yesterday']) * 100 : 0; return [ 'value' => round($todayVal, 2), 'yesterday' => round($yesterdayVal, 2), 'change' => $this->calculatePercentageChange($todayVal, $yesterdayVal), ]; } }