145 lines
6.1 KiB
PHP
145 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Pages;
|
|
|
|
use App\Enums\RoleEnum;
|
|
use App\Models\Customer;
|
|
use App\Models\DelayedGood;
|
|
use App\Models\EggCollectionItem;
|
|
use App\Models\Expense;
|
|
use App\Models\FeedPurchase;
|
|
use App\Models\Order;
|
|
use App\Models\Payroll;
|
|
use Filament\Pages\Dashboard as BaseDashboard;
|
|
use Filament\Widgets\AccountWidget;
|
|
|
|
class Dashboard extends BaseDashboard
|
|
{
|
|
protected static ?string $title = 'Dasbor';
|
|
|
|
protected string $view = 'filament.pages.dashboard';
|
|
|
|
public function getHeaderWidgets(): array
|
|
{
|
|
return [
|
|
AccountWidget::class,
|
|
];
|
|
}
|
|
|
|
public function getViewData(): array
|
|
{
|
|
$formatNumber = fn ($val) => number_format((float) $val, $val == floor($val) ? 0 : 2, ',', '.');
|
|
$now = now();
|
|
$today = $now->format('Y-m-d');
|
|
|
|
// 1. Egg Production by Unit - Single optimized query with joins
|
|
$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')
|
|
->where('egg_collections.production_date', $today)
|
|
->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),
|
|
]);
|
|
|
|
// 2. Orders Stats - Combined count and sum
|
|
$orderStats = Order::whereDate('order_date', $today)
|
|
->selectRaw('count(*) as count, sum(total_amount) as total')
|
|
->first();
|
|
$orderCount = (int) ($orderStats->count ?? 0);
|
|
$orderRevenue = (float) ($orderStats->total ?? 0);
|
|
|
|
// 3. Delayed Goods Stats - Combined sum and paid_amount
|
|
$delayedStatsByUnit = DelayedGood::query()
|
|
->join('units', 'delayed_goods.unit_id', 'units.id')
|
|
->whereDate('delayed_goods.stored_date', $today)
|
|
->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),
|
|
]);
|
|
|
|
$delayedStatsTotals = DelayedGood::whereDate('stored_date', $today)
|
|
->selectRaw('sum(total_amount) as total, sum(paid_amount) as paid')
|
|
->first();
|
|
|
|
$delayedRevenue = (float) ($delayedStatsTotals->total ?? 0);
|
|
$paidToday = (float) ($delayedStatsTotals->paid ?? 0);
|
|
$totalRevenue = $orderRevenue + $delayedRevenue;
|
|
|
|
// 4. Expenses & Financials Today
|
|
$totalExpenses = (float) Expense::whereDate('expense_date', $today)->sum('amount');
|
|
$monthlyPayroll = (float) Payroll::where('period_month', $now->format('Y-m'))->sum('total_salary');
|
|
$totalFeedPurchase = (float) FeedPurchase::whereDate('purchase_date', $today)->sum('total_price');
|
|
|
|
$grossProfit = $totalRevenue - $totalFeedPurchase;
|
|
$netProfit = $grossProfit - $totalExpenses - $monthlyPayroll;
|
|
$totalReceivablesToday = $delayedRevenue - $paidToday;
|
|
|
|
$mainStats = [
|
|
['label' => 'Pesanan', 'desc' => 'Jumlah transaksi', 'value' => $orderCount, 'prefix' => ''],
|
|
['label' => 'Laba Bersih', 'desc' => 'Setelah dikurangi pengeluaran harian', 'value' => $formatNumber($netProfit), 'prefix' => 'Rp'],
|
|
['label' => 'Piutang', 'desc' => 'Piutang barang tertunda', 'value' => $formatNumber($totalReceivablesToday), 'prefix' => 'Rp'],
|
|
['label' => 'Pendapatan', 'desc' => 'Akumulasi pemasukan', 'value' => $formatNumber($totalRevenue), 'prefix' => 'Rp'],
|
|
['label' => 'Belanja Pakan', 'desc' => 'Baru dibeli', 'value' => $formatNumber($totalFeedPurchase), 'prefix' => 'Rp'],
|
|
];
|
|
|
|
// 5. Top Customers Today - Optimized summing
|
|
$topCustomers = Customer::query()
|
|
->select('id', 'name', 'phone_number')
|
|
->withSum(['orders' => fn ($q) => $q->whereDate('order_date', $today)], 'total_amount')
|
|
->withSum(['delayedGoods' => fn ($q) => $q->whereDate('stored_date', $today)], 'total_amount')
|
|
->get()
|
|
->map(function ($customer) {
|
|
$customer->total_spent = ($customer->orders_sum_total_amount ?? 0) + ($customer->delayed_goods_sum_total_amount ?? 0);
|
|
|
|
return $customer;
|
|
})
|
|
->filter(fn ($c) => $c->total_spent > 0)
|
|
->sortByDesc('total_spent')
|
|
->take(5)
|
|
->values();
|
|
|
|
// 6. Distribution Charts Data
|
|
$revenueSplitData = [
|
|
'labels' => ['Penjualan Langsung', 'Barang Tertunda'],
|
|
'data' => [$orderRevenue, $delayedRevenue],
|
|
];
|
|
|
|
$expenseDistributionData = [
|
|
'labels' => ['Belanja Pakan', 'Pengeluaran Umum'],
|
|
'data' => [$totalFeedPurchase, $totalExpenses],
|
|
];
|
|
|
|
$salesByUnit = Order::whereDate('order_date', $today)
|
|
->join('units', 'orders.unit_id', 'units.id')
|
|
->selectRaw('units.name, sum(total_amount) as total')
|
|
->groupBy('units.name')
|
|
->get();
|
|
|
|
return [
|
|
'isAdministrator' => auth()->user()->hasRole(RoleEnum::ADMINISTRATOR->value),
|
|
'mainStats' => $mainStats,
|
|
'totalEggsByUnit' => $totalEggsByUnit,
|
|
'totalDelayedByUnit' => $delayedStatsByUnit,
|
|
'topCustomers' => $topCustomers,
|
|
'revenueSplit' => $revenueSplitData,
|
|
'expenseDistribution' => $expenseDistributionData,
|
|
'salesByUnit' => [
|
|
'labels' => $salesByUnit->pluck('name')->values(),
|
|
'data' => $salesByUnit->pluck('total')->map(fn ($v) => (float) $v)->values(),
|
|
],
|
|
'formatNumber' => $formatNumber,
|
|
];
|
|
}
|
|
}
|