131 lines
4.9 KiB
PHP
131 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\CategoryType;
|
|
use App\Models\Account;
|
|
use App\Models\Transaction;
|
|
use App\Services\AccountMetricsService;
|
|
use App\Services\CashflowSummaryService;
|
|
use App\Services\CategorySpendingService;
|
|
use App\Services\PeriodComparator;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class DashboardController extends Controller
|
|
{
|
|
public function __construct(
|
|
private AccountMetricsService $accountMetricsService,
|
|
private CategorySpendingService $categorySpendingService,
|
|
) {}
|
|
|
|
public function __invoke(Request $request): Response
|
|
{
|
|
return Inertia::render('dashboard', [
|
|
'showEncryptionPrompt' => session('show_encryption_prompt', false),
|
|
'netWorthEvolution' => Inertia::defer(fn () => $this->getNetWorthEvolution($request), 'dashboard'),
|
|
'topCategories' => Inertia::defer(fn () => $this->getTopCategories($request), 'dashboard'),
|
|
'cashflowSummary' => Inertia::defer(fn () => $this->getCashflowSummary($request), 'dashboard'),
|
|
]);
|
|
}
|
|
|
|
private function getNetWorthEvolution(Request $request): array
|
|
{
|
|
$user = $request->user();
|
|
$now = Carbon::now();
|
|
$start = $now->copy()->subMonths(12);
|
|
$end = $now->copy();
|
|
|
|
$accounts = Account::query()
|
|
->where('user_id', $user->id)
|
|
->with(['bank:id,name,logo', 'realEstateDetail:account_id,linked_loan_account_id'])
|
|
->orderBy('position')
|
|
->orderBy('name')
|
|
->get();
|
|
|
|
return $this->accountMetricsService->getNetWorthEvolution($user->currency_code, $accounts, $start, $end);
|
|
}
|
|
|
|
private function getTopCategories(Request $request): array
|
|
{
|
|
$user = $request->user();
|
|
$now = Carbon::now();
|
|
$from = $now->copy()->subDays(30);
|
|
$to = $now->copy();
|
|
|
|
$period = new PeriodComparator($from, $to);
|
|
$previousPeriod = $period->previous();
|
|
|
|
$currentSpending = $this->categorySpendingService->forPeriod($user->id, $period->from, $period->to);
|
|
$previousSpending = $this->categorySpendingService->forPeriod($user->id, $previousPeriod->from, $previousPeriod->to);
|
|
|
|
$totalAmount = $currentSpending->sum('amount');
|
|
|
|
return $currentSpending
|
|
->sortByDesc('amount')
|
|
->take(10)
|
|
->map(function ($item) use ($previousSpending, $totalAmount) {
|
|
$previousAmount = $previousSpending->firstWhere('category_id', $item['category_id'])['amount'] ?? 0;
|
|
|
|
return [
|
|
'category' => $item['category'],
|
|
'category_id' => $item['category_id'],
|
|
'amount' => $item['amount'],
|
|
'previous_amount' => $previousAmount,
|
|
'total_amount' => $totalAmount,
|
|
'has_children' => $item['has_children'],
|
|
'is_direct' => $item['is_direct'],
|
|
];
|
|
})
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
private function getCashflowSummary(Request $request): array
|
|
{
|
|
$user = $request->user();
|
|
$now = Carbon::now();
|
|
$from = $now->copy()->startOfMonth();
|
|
$to = $now->copy()->endOfMonth();
|
|
|
|
$period = new PeriodComparator($from, $to);
|
|
$previousPeriod = $period->previous();
|
|
|
|
return [
|
|
'current' => $this->calculateCashflowSummary($user->id, $period->from, $period->to),
|
|
'previous' => $this->calculateCashflowSummary($user->id, $previousPeriod->from, $previousPeriod->to),
|
|
];
|
|
}
|
|
|
|
private function calculateCashflowSummary(string $userId, Carbon $from, Carbon $to): array
|
|
{
|
|
$income = max(0, $this->getTransactionSum($userId, $from, $to, CategoryType::Income));
|
|
$expense = max(0, -$this->getTransactionSum($userId, $from, $to, CategoryType::Expense));
|
|
|
|
return CashflowSummaryService::summarize($income, $expense);
|
|
}
|
|
|
|
private function getTransactionSum(string $userId, Carbon $from, Carbon $to, CategoryType $type): int
|
|
{
|
|
return Transaction::query()
|
|
->where('transactions.user_id', $userId)
|
|
->whereBetween('transactions.transaction_date', [$from, $to])
|
|
->where(function ($q) use ($type) {
|
|
$q->whereExists(function ($sub) use ($type) {
|
|
$sub->select(DB::raw(1))
|
|
->from('categories')
|
|
->whereColumn('categories.id', 'transactions.category_id')
|
|
->where('categories.type', $type);
|
|
})
|
|
->orWhere(function ($q) use ($type) {
|
|
$q->whereNull('transactions.category_id')
|
|
->where('transactions.amount', $type === CategoryType::Income ? '>' : '<', 0);
|
|
});
|
|
})
|
|
->sum('transactions.amount');
|
|
}
|
|
}
|