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([ Grid::make(3) ->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'); }), 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(), ]) ->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. Count customers $countPelanggan = Customer::count(); // 2. Count total eggs (by unit) $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. Count total orders $countPesanan = Order::whereBetween('order_date', [$fromDate, $toDate])->count(); // 4. Count total delayed goods (by unit) $totalDelayedByUnit = 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. Revenue $pendapatanPesanan = Order::whereBetween('order_date', [$fromDate, $toDate])->sum('total_amount'); $pendapatanTertunda = DelayedGood::whereBetween('stored_date', [$fromDate, $toDate])->sum('total_amount'); $totalPendapatan = $pendapatanPesanan + $pendapatanTertunda; // 6. Financial totals $totalPengeluaran = Expense::whereBetween('expense_date', [$fromDate, $toDate])->sum('amount'); $totalPenggajian = Payroll::whereBetween('period_month', [Carbon::parse($fromDate)->format('Y-m'), Carbon::parse($toDate)->format('Y-m')])->sum('total_salary'); $totalBelanjaPakan = FeedPurchase::whereBetween('purchase_date', [$fromDate, $toDate])->sum('total_price'); $labaKotor = $totalPendapatan - $totalBelanjaPakan; $labaBersih = $labaKotor - $totalPengeluaran - $totalPenggajian; $paidInRange = DelayedGood::whereBetween('stored_date', [$fromDate, $toDate])->sum('paid_amount'); $totalInRange = DelayedGood::whereBetween('stored_date', [$fromDate, $toDate])->sum('total_amount'); $totalPiutang = $totalInRange - $paidInRange; $mainStats = [ ['label' => 'Total Pelanggan', 'desc' => 'Jumlah pelanggan terdaftar', 'value' => $countPelanggan, 'prefix' => ''], ['label' => 'Total Pesanan', 'desc' => 'Jumlah transaksi pesanan', 'value' => $countPesanan, 'prefix' => ''], ['label' => 'Laba Bersih', 'desc' => 'Setelah dikurangi beban operasional', 'value' => $formatNumber($labaBersih), 'prefix' => 'Rp'], ['label' => 'Total Piutang', 'desc' => 'Piutang barang tertunda (periode)', 'value' => $formatNumber($totalPiutang), 'prefix' => 'Rp'], ['label' => 'Total Pendapatan', 'desc' => 'Akumulasi semua pemasukan', 'value' => $formatNumber($totalPendapatan), 'prefix' => 'Rp'], ['label' => 'Laba Kotor', 'desc' => 'Pendapatan dikurangi biaya pakan', 'value' => $formatNumber($labaKotor), 'prefix' => 'Rp'], ['label' => 'Total Pengeluaran', 'desc' => 'Biaya operasional & umum', 'value' => $formatNumber($totalPengeluaran), 'prefix' => 'Rp'], ['label' => 'Total Penggajian', 'desc' => 'Total gaji karyawan', 'value' => $formatNumber($totalPenggajian), 'prefix' => 'Rp'], ['label' => 'Belanja Pakan', 'desc' => 'Total pembelian pakan ayam', 'value' => $formatNumber($totalBelanjaPakan), 'prefix' => 'Rp'], ]; // Top 5 Customers by Order Amount in Range $topCustomers = Customer::query() ->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 (In range) $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(); $productionChart = [ 'labels' => $rangeQueryDays->map(fn ($d) => Carbon::parse($d)->format('d M'))->values(), 'good' => $rangeQueryDays->map(fn ($d) => (float) EggCollection::whereDate('production_date', $d)->sum('total_eggs'))->values(), 'broken' => $rangeQueryDays->map(fn ($d) => (float) EggCollection::whereDate('production_date', $d)->sum('total_broken_eggs'))->values(), ]; // 8. Financial Trends (Monthly - 6 months prior to to_date) $months = collect(range(0, 5))->map(fn ($i) => Carbon::parse($toDate)->subMonths($i)->format('Y-m'))->reverse(); $financialChart = [ 'labels' => $months->map(fn ($m) => Carbon::parse($m)->translatedFormat('M Y'))->values(), 'revenue' => $months->map(function ($m) { $orderRev = Order::whereDate('order_date', 'like', "$m%")->sum('total_amount'); $delayedRev = DelayedGood::whereDate('stored_date', 'like', "$m%")->sum('total_amount'); return (float) ($orderRev + $delayedRev); })->values(), 'expenses' => $months->map(function ($m) { $exp = Expense::whereDate('expense_date', 'like', "$m%")->sum('amount'); $payroll = Payroll::where('period_month', 'like', "$m%")->sum('total_salary'); $feed = FeedPurchase::whereDate('purchase_date', 'like', "$m%")->sum('total_price'); return (float) ($exp + $payroll + $feed); })->values(), ]; // 8b. Chart: Monthly Profit Trend $financialChart['profit'] = collect($financialChart['revenue'])->map(fn ($rev, $i) => (float) ($rev - $financialChart['expenses'][$i]))->values(); // 9. Chart: Expense Distribution $expenseDistribution = [ 'labels' => ['Gaji Karyawan', 'Belanja Pakan', 'Pengeluaran Umum'], 'data' => [(float) $totalPenggajian, (float) $totalBelanjaPakan, (float) $totalPengeluaran], ]; // 10. Chart: Sales Distribution by Unit $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(); // 11. Chart: Debt Status (In range) $debtStatus = [ 'paid' => (float) $paidInRange, 'unpaid' => (float) ($totalInRange - $paidInRange), ]; // 12. Chart: Feed Stocks (Current) $feedStocks = Feed::query() ->join('units', 'feeds.unit_id', '=', 'units.id') ->select('feeds.name', 'feeds.stock', 'units.alias as unit_alias') ->get(); // 13. Chart: Production by Warehouse (In range) $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(); // 14. Chart: Revenue Split (In range) $revenueSplit = [ 'orders' => (float) $pendapatanPesanan, 'delayed' => (float) $pendapatanTertunda, ]; return [ 'mainStats' => $mainStats, 'totalEggsByUnit' => $totalEggsByUnit, 'totalDelayedByUnit' => $totalDelayedByUnit, 'topCustomers' => $topCustomers, 'productionChart' => $productionChart, 'financialChart' => $financialChart, 'expenseDistribution' => $expenseDistribution, 'salesByUnit' => [ 'labels' => $salesByUnit->pluck('name')->values(), 'data' => $salesByUnit->pluck('total')->map(fn ($v) => (float) $v)->values(), ], 'debtStatus' => $debtStatus, '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' => [$revenueSplit['orders'], $revenueSplit['delayed']], ], 'formatNumber' => $formatNumber, ]; } }