dress/app/Http/Controllers/AnalysisController.php

188 lines
6.4 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Enums\OrderChannel;
use App\Enums\OrderStatus;
use App\Enums\PaymentMethod;
use App\Models\Expense;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Purchase;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class AnalysisController extends Controller
{
public function __invoke()
{
$stats = [
'total_sales' => $this->getStats(fn() => Order::count()),
'total_revenue' => $this->getStats(fn() => Order::sum('total')),
'total_expenses' => $this->getStats(fn() => Expense::sum('amount')),
'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'] = $this->calculateDiff($stats['gross_profit'], $stats['total_expenses']);
$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();
$ordersByHourRaw = DB::table('orders')
->select(
DB::raw('HOUR(created_at) as hour'),
DB::raw('COUNT(*) as total')
)
->groupBy(DB::raw('HOUR(created_at)'))
->get();
$ordersByHour = collect(range(0, 23))->map(function ($hour) use ($ordersByHourRaw) {
$found = $ordersByHourRaw->firstWhere('hour', $hour);
return [
'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT) . ':00',
'total' => $found ? (int) $found->total : 0,
];
});
return Inertia::render('analysis', [
'stats' => $stats,
'salesByMonth' => $salesByMonth,
'ordersByHour' => $ordersByHour,
'paymentMethods' => $paymentMethods,
'orderStatuses' => $orderStatuses,
'orderChannels' => $orderChannels,
'topProducts' => $topProducts,
'topCustomers' => $topCustomers,
]);
}
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,
];
}
}