$this->getStats(fn () => Order::count()), 'total_revenue' => $this->getStats(fn () => Order::sum('total')), 'total_expenses' => $this->getStats(fn () => Expense::sum('amount')), 'total_payrolls' => $this->getStats(fn () => Payroll::sum('total_salary')), 'cogs' => $this->getStats(fn () => Order::sum('hpp')), 'total_discount' => $this->getStats(fn () => Order::sum('discount')), 'total_purchases' => $this->getStats(fn () => Purchase::sum('total')), 'products_sold' => $this->getStats(fn () => OrderItem::sum('qty')), 'total_customers' => $this->getStats(fn () => Order::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'] = [ 'value' => $stats['gross_profit']['value'] - $stats['total_expenses']['value'] - $stats['total_payrolls']['value'], 'yesterday' => null, 'change' => null, ]; $stats['profit_margin'] = $this->calculateMargin($stats['net_profit'], $stats['total_revenue']); // Revenue vs Purchases per month (this year) $revenueByMonth = DB::table('orders') ->select( DB::raw('MONTH(created_at) as month'), DB::raw('SUM(total) as total') ) ->groupBy(DB::raw('MONTH(created_at)')) ->get(); $purchasesByMonth = DB::table('purchases') ->select( DB::raw('MONTH(created_at) as month'), DB::raw('SUM(total) as total') ) ->groupBy(DB::raw('MONTH(created_at)')) ->get(); $monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; $salesByMonth = collect(range(1, 12))->map(function ($month) use ($revenueByMonth, $purchasesByMonth, $monthNames) { $revenueFound = $revenueByMonth->firstWhere('month', $month); $purchasesFound = $purchasesByMonth->firstWhere('month', $month); return [ 'month' => $monthNames[$month - 1], 'sales' => $revenueFound ? (float) $revenueFound->total : 0, 'purchase' => $purchasesFound ? (float) $purchasesFound->total : 0, ]; }); $paymentMethods = DB::table('orders') ->select('payment_method as name', DB::raw('COUNT(*) as total')) ->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')) ->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')) ->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') ->select('products.name as name', DB::raw('SUM(order_items.qty) as total')) ->groupBy('products.name') ->orderByDesc('total') ->limit(5) ->get(); $topCustomers = DB::table('orders') ->select('customer_name as name', DB::raw('SUM(total) as total')) ->whereNotNull('customer_name') ->groupBy('customer_name') ->orderByDesc('total') ->limit(5) ->get(); $salesByHourRaw = DB::table('orders') ->select( DB::raw('HOUR(created_at) as hour'), DB::raw('COUNT(*) as total') ) ->groupBy(DB::raw('HOUR(created_at)')) ->get(); $salesByHour = collect(range(0, 23))->map(function ($hour) use ($salesByHourRaw) { $found = $salesByHourRaw->firstWhere('hour', $hour); return [ 'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT).':00', 'total' => $found ? (int) $found->total : 0, ]; }); // Revenue & Profit & Volume per month $ordersByMonth = DB::table('orders') ->select( DB::raw('MONTH(created_at) as month'), DB::raw('SUM(total) as revenue'), DB::raw('SUM(hpp) as cogs'), DB::raw('COUNT(*) as count') ) ->groupBy(DB::raw('MONTH(created_at)')) ->get(); $expensesByMonth = DB::table('expenses') ->select( DB::raw('MONTH(created_at) as month'), DB::raw('SUM(amount) as total') ) ->groupBy(DB::raw('MONTH(created_at)')) ->get(); $payrollsByMonth = DB::table('payrolls') ->select( DB::raw('MONTH(period_month) as month'), DB::raw('SUM(total_salary) as total') ) ->whereNull('deleted_at') ->groupBy(DB::raw('MONTH(period_month)')) ->get(); $revenueVsProfit = collect(range(1, 12))->map(function ($month) use ($ordersByMonth, $expensesByMonth, $payrollsByMonth, $monthNames) { $orderFound = $ordersByMonth->firstWhere('month', $month); $expenseFound = $expensesByMonth->firstWhere('month', $month); $payrollFound = $payrollsByMonth->firstWhere('month', $month); $revenue = $orderFound ? (float) $orderFound->revenue : 0; $cogs = $orderFound ? (float) $orderFound->cogs : 0; $expense = $expenseFound ? (float) $expenseFound->total : 0; $payroll = $payrollFound ? (float) $payrollFound->total : 0; $profit = $revenue - $cogs - $expense - $payroll; return [ 'month' => $monthNames[$month - 1], 'revenue' => $revenue, 'profit' => $profit, ]; }); $transactionVolume = collect(range(1, 12))->map(function ($month) use ($ordersByMonth, $monthNames) { $found = $ordersByMonth->firstWhere('month', $month); $count = $found ? (int) $found->count : 0; $revenue = $found ? (float) $found->revenue : 0; $aov = $count > 0 ? $revenue / $count : 0; return [ 'month' => $monthNames[$month - 1], 'count' => $count, 'aov' => $aov, ]; }); $topCategories = DB::table('order_items') ->join('products', 'order_items.product_id', '=', 'products.id') ->join('category_product', 'products.id', '=', 'category_product.product_id') ->join('categories', 'category_product.category_id', '=', 'categories.id') ->select('categories.name', DB::raw('SUM(order_items.qty) as total')) ->whereNull('order_items.deleted_at') ->groupBy('categories.id', 'categories.name') ->orderByDesc('total') ->limit(5) ->get(); return Inertia::render('analysis', [ 'stats' => $stats, 'salesByMonth' => $salesByMonth, 'revenueVsProfit' => $revenueVsProfit, 'transactionVolume' => $transactionVolume, 'salesByHour' => $salesByHour, 'paymentMethods' => $paymentMethods, 'orderStatuses' => $orderStatuses, 'orderChannels' => $orderChannels, 'topProducts' => $topProducts, 'topCustomers' => $topCustomers, 'topCategories' => $topCategories, ]); } private function getStats(callable $callback): array { $value = $callback(); return [ 'value' => $value, 'yesterday' => null, 'change' => null, ]; } private function calculateAov($revenue, $sales): array { $value = $sales['value'] > 0 ? $revenue['value'] / $sales['value'] : 0; return [ 'value' => $value, 'yesterday' => null, 'change' => null, ]; } private function calculateDiff($a, $b): array { return [ 'value' => $a['value'] - $b['value'], 'yesterday' => null, 'change' => null, ]; } private function calculateMargin($profit, $revenue): array { $value = $revenue['value'] > 0 ? ($profit['value'] / $revenue['value']) * 100 : 0; return [ 'value' => round($value, 2), 'yesterday' => null, 'change' => null, ]; } }