form->fill([ 'period' => 'this_month', 'from_date' => now()->startOfMonth()->format('d F Y'), 'to_date' => now()->format('d F Y'), ]); } public function form(Schema $form): Schema { return $form ->schema([ Section::make() ->schema([ Select::make('period') ->label('Periode Cepat') ->options([ 'today' => 'Hari Ini', 'this_week' => 'Minggu Ini', 'this_month' => 'Bulan Ini', 'this_year' => 'Tahun Ini', 'custom' => 'Kustom Tanggal', ]) ->live() ->afterStateUpdated(function ($state, $set, $get) { if ($state === 'today') { $set('from_date', now()->format('d F Y')); $set('to_date', now()->format('d F Y')); } elseif ($state === 'this_week') { $set('from_date', now()->startOfWeek()->format('d F Y')); $set('to_date', now()->endOfWeek()->format('d F Y')); } elseif ($state === 'this_month') { $set('from_date', now()->startOfMonth()->format('d F Y')); $set('to_date', now()->endOfMonth()->format('d F Y')); } elseif ($state === 'this_year') { $set('from_date', now()->startOfYear()->format('d F Y')); $set('to_date', now()->endOfYear()->format('d F Y')); } $this->dispatch('stats-updated'); }) ->native(false), DatePicker::make('from_date') ->label('Dari Tanggal') ->native(false) ->displayFormat('d F Y') ->live() ->afterStateUpdated(function ($set) { $set('period', 'custom'); $this->dispatch('stats-updated'); }), DatePicker::make('to_date') ->label('Sampai Tanggal') ->native(false) ->displayFormat('d F Y') ->live() ->afterStateUpdated(function ($set) { $set('period', 'custom'); $this->dispatch('stats-updated'); }), ]) ->compact() ->columns(3), ]) ->statePath('data'); } protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedChartPie; protected static ?string $navigationLabel = 'Analisis'; protected static ?string $slug = 'analisys'; protected static ?string $title = 'Analisis'; protected string $view = 'filament.pages.analisys'; public function getViewData(): array { $formatNumber = fn ($val) => number_format((float) $val, $val == floor($val) ? 0 : 2, ',', '.'); $now = now(); $fromDateInput = $this->data['from_date'] ?? now()->startOfMonth()->format('d F Y'); $toDateInput = $this->data['to_date'] ?? now()->format('d F Y'); // Normalize for DB queries $fromDate = Carbon::parse($fromDateInput)->format('Y-m-d'); $toDate = Carbon::parse($toDateInput)->format('Y-m-d'); // 1. Customer Count $customerCount = Customer::count(); // 2. Egg Production by Unit - Optimized join query $totalEggsByUnit = EggCollectionItem::query() ->join('egg_collections', 'egg_collection_items.egg_collection_id', '=', 'egg_collections.id') ->join('units', 'egg_collection_items.unit_id', '=', 'units.id') ->whereNull('egg_collections.deleted_at') ->whereBetween('egg_collections.production_date', [$fromDate, $toDate]) ->selectRaw('units.name as unit_name, sum(case when is_broken = 0 then quantity else 0 end) as total_good, sum(case when is_broken = 1 then quantity else 0 end) as total_broken') ->groupBy('units.name') ->get() ->map(fn ($item) => [ 'unit_name' => $item->unit_name, 'total_good' => $formatNumber($item->total_good), 'total_broken' => $formatNumber($item->total_broken), ]); // 3. Orders Stats - Combined count and sum $orderStats = Order::whereBetween('order_date', [$fromDate, $toDate]) ->selectRaw('count(*) as count, sum(total_amount) as total') ->first(); $orderCount = (int) ($orderStats->count ?? 0); $orderRevenue = (float) ($orderStats->total ?? 0); // 4. Delayed Goods Stats - Combined by unit $delayedStatsByUnit = DelayedGood::query() ->join('units', 'delayed_goods.unit_id', '=', 'units.id') ->whereBetween('delayed_goods.stored_date', [$fromDate, $toDate]) ->selectRaw('units.name as unit_name, sum(quantity) as total') ->groupBy('units.name') ->get() ->map(fn ($item) => [ 'unit_name' => $item->unit_name, 'total' => $formatNumber($item->total), ]); // 5. Financial Totals in Range $delayedTotals = DelayedGood::whereBetween('stored_date', [$fromDate, $toDate]) ->selectRaw('sum(total_amount) as total, sum(paid_amount) as paid') ->first(); $delayedRevenue = (float) ($delayedTotals->total ?? 0); $paidAmountInRange = (float) ($delayedTotals->paid ?? 0); $totalRevenue = $orderRevenue + $delayedRevenue; $totalExpenses = (float) Expense::whereBetween('expense_date', [$fromDate, $toDate])->sum('amount'); $totalPayroll = (float) Payroll::whereBetween('period_month', [Carbon::parse($fromDate)->format('Y-m'), Carbon::parse($toDate)->format('Y-m')])->sum('total_salary'); $totalFeedPurchase = (float) FeedPurchase::whereBetween('purchase_date', [$fromDate, $toDate])->sum('total_price'); $grossProfit = $totalRevenue - $totalFeedPurchase; $netProfit = $grossProfit - $totalExpenses - $totalPayroll; $totalReceivables = $delayedRevenue - $paidAmountInRange; $mainStats = [ ['label' => 'Total Pelanggan', 'desc' => 'Jumlah pelanggan terdaftar', 'value' => $customerCount, 'prefix' => ''], ['label' => 'Total Pesanan', 'desc' => 'Jumlah transaksi pesanan', 'value' => $orderCount, 'prefix' => ''], ['label' => 'Laba Bersih', 'desc' => 'Setelah dikurangi beban operasional', 'value' => $formatNumber($netProfit), 'prefix' => 'Rp'], ['label' => 'Total Piutang', 'desc' => 'Piutang barang tertunda (periode)', 'value' => $formatNumber($totalReceivables), 'prefix' => 'Rp'], ['label' => 'Total Pendapatan', 'desc' => 'Akumulasi semua pemasukan', 'value' => $formatNumber($totalRevenue), 'prefix' => 'Rp'], ['label' => 'Laba Kotor', 'desc' => 'Pendapatan dikurangi biaya pakan', 'value' => $formatNumber($grossProfit), 'prefix' => 'Rp'], ['label' => 'Total Pengeluaran', 'desc' => 'Biaya operasional & umum', 'value' => $formatNumber($totalExpenses), 'prefix' => 'Rp'], ['label' => 'Total Penggajian', 'desc' => 'Total gaji karyawan', 'value' => $formatNumber($totalPayroll), 'prefix' => 'Rp'], ['label' => 'Belanja Pakan', 'desc' => 'Total pembelian pakan ayam', 'value' => $formatNumber($totalFeedPurchase), 'prefix' => 'Rp'], ]; // 6. Top 5 Customers - Optimized summing $topCustomers = Customer::query() ->select('id', 'name', 'phone_number') ->withSum(['orders' => fn ($q) => $q->whereBetween('order_date', [$fromDate, $toDate])], 'total_amount') ->withSum(['delayedGoods' => fn ($q) => $q->whereBetween('stored_date', [$fromDate, $toDate])], 'total_amount') ->get() ->map(function ($customer) { $customer->total_spent = ($customer->orders_sum_total_amount ?? 0) + ($customer->delayed_goods_sum_total_amount ?? 0); return $customer; }) ->sortByDesc('total_spent') ->take(5) ->values(); // 7. Chart: Daily Production (Optimized query) $diffDays = Carbon::parse($fromDate)->diffInDays(Carbon::parse($toDate)); $rangeQueryDays = collect(range(0, min($diffDays, 30)))->map(fn ($i) => Carbon::parse($fromDate)->addDays($i)->format('Y-m-d'))->values(); $productionDataMap = EggCollection::whereBetween('production_date', [$fromDate, $toDate]) ->selectRaw('production_date, sum(total_eggs) as good, sum(total_broken_eggs) as broken') ->groupBy('production_date') ->get() ->keyBy('production_date'); $productionChartData = [ 'labels' => $rangeQueryDays->map(fn ($d) => Carbon::parse($d)->format('d M'))->values(), 'good' => $rangeQueryDays->map(fn ($d) => (float) ($productionDataMap[$d]->good ?? 0))->values(), 'broken' => $rangeQueryDays->map(fn ($d) => (float) ($productionDataMap[$d]->broken ?? 0))->values(), ]; // 8. Monthly Financial Trends (Optimized aggregates) $months = collect(range(0, 5))->map(fn ($i) => Carbon::parse($toDate)->subMonths($i)->format('Y-m'))->reverse(); $monthlyOrderRev = Order::whereIn(DB::raw("DATE_FORMAT(order_date, '%Y-%m')"), $months) ->selectRaw("DATE_FORMAT(order_date, '%Y-%m') as month, sum(total_amount) as total") ->groupBy('month')->pluck('total', 'month'); $monthlyDelayedRev = DelayedGood::whereIn(DB::raw("DATE_FORMAT(stored_date, '%Y-%m')"), $months) ->selectRaw("DATE_FORMAT(stored_date, '%Y-%m') as month, sum(total_amount) as total") ->groupBy('month')->pluck('total', 'month'); $monthlyExpenses = Expense::whereIn(DB::raw("DATE_FORMAT(expense_date, '%Y-%m')"), $months) ->selectRaw("DATE_FORMAT(expense_date, '%Y-%m') as month, sum(amount) as total") ->groupBy('month')->pluck('total', 'month'); $monthlyPayroll = Payroll::whereIn('period_month', $months) ->selectRaw('period_month as month, sum(total_salary) as total') ->groupBy('month')->pluck('total', 'month'); $monthlyFeed = FeedPurchase::whereIn(DB::raw("DATE_FORMAT(purchase_date, '%Y-%m')"), $months) ->selectRaw("DATE_FORMAT(purchase_date, '%Y-%m') as month, sum(total_price) as total") ->groupBy('month')->pluck('total', 'month'); $financialChartData = [ 'labels' => $months->map(fn ($m) => Carbon::parse($m)->translatedFormat('M Y'))->values(), 'revenue' => $months->map(fn ($m) => (float) (($monthlyOrderRev[$m] ?? 0) + ($monthlyDelayedRev[$m] ?? 0)))->values(), 'expenses' => $months->map(fn ($m) => (float) (($monthlyExpenses[$m] ?? 0) + ($monthlyPayroll[$m] ?? 0) + ($monthlyFeed[$m] ?? 0)))->values(), ]; $financialChartData['profit'] = $financialChartData['revenue']->map(fn ($rev, $i) => (float) ($rev - $financialChartData['expenses'][$i]))->values(); // 9. Distribution Charts $expenseDistributionData = [ 'labels' => ['Gaji Karyawan', 'Belanja Pakan', 'Pengeluaran Umum'], 'data' => [(float) $totalPayroll, (float) $totalFeedPurchase, (float) $totalExpenses], ]; $salesByUnit = Order::whereBetween('order_date', [$fromDate, $toDate]) ->join('units', 'orders.unit_id', '=', 'units.id') ->selectRaw('units.name, sum(total_amount) as total') ->groupBy('units.name') ->get(); $debtStatusData = [ 'paid' => (float) $paidAmountInRange, 'unpaid' => (float) ($delayedRevenue - $paidAmountInRange), ]; $feedStocks = Feed::query() ->join('units', 'feeds.unit_id', '=', 'units.id') ->select('feeds.name', 'feeds.stock', 'units.alias as unit_alias') ->get(); $productionByWarehouse = EggCollection::whereBetween('production_date', [$fromDate, $toDate]) ->join('warehouses', 'egg_collections.warehouse_id', '=', 'warehouses.id') ->selectRaw('warehouses.name, sum(total_eggs) as total') ->groupBy('warehouses.name') ->get(); $revenueSplitData = [ 'orders' => (float) $orderRevenue, 'delayed' => (float) $delayedRevenue, ]; return [ 'mainStats' => $mainStats, 'totalEggsByUnit' => $totalEggsByUnit, 'totalDelayedByUnit' => $delayedStatsByUnit, 'topCustomers' => $topCustomers, 'productionChart' => $productionChartData, 'financialChart' => $financialChartData, 'expenseDistribution' => $expenseDistributionData, 'salesByUnit' => [ 'labels' => $salesByUnit->pluck('name')->values(), 'data' => $salesByUnit->pluck('total')->map(fn ($v) => (float) $v)->values(), ], 'debtStatus' => $debtStatusData, 'feedStocks' => [ 'labels' => $feedStocks->pluck('name')->values(), 'data' => $feedStocks->pluck('stock')->map(fn ($v) => (float) $v)->values(), 'units' => $feedStocks->pluck('unit_alias')->values(), ], 'productionByWarehouse' => [ 'labels' => $productionByWarehouse->pluck('name')->values(), 'data' => $productionByWarehouse->pluck('total')->map(fn ($v) => (float) $v)->values(), ], 'revenueSplit' => [ 'labels' => ['Penjualan Langsung', 'Barang Tertunda'], 'data' => [$revenueSplitData['orders'], $revenueSplitData['delayed']], ], 'formatNumber' => $formatNumber, ]; } }