date = now()->toDateString(); $this->dateEffective = now()->toDateString(); } public function addTransaction(): void { $this->validate([ 'date' => ['required', 'date'], 'dateEffective' => ['required', 'date'], 'categoryId' => ['required', 'exists:categories,id'], 'amount' => ['required', 'numeric'], 'details' => ['nullable', 'string', 'max:255'], ]); $household = auth()->user()->currentHousehold; $category = $household->categories()->findOrFail($this->categoryId); $household->transactions()->create([ 'category_id' => $category->id, 'user_id' => auth()->id(), 'date' => $this->date, 'date_effective' => $this->dateEffective, 'amount' => $this->amount, 'details' => $this->details, ]); $this->reset(['categoryId', 'amount', 'details']); $this->date = now()->toDateString(); $this->dateEffective = now()->toDateString(); $this->dispatch('transaction-added'); } public function deleteTransaction(int $transactionId): void { $household = auth()->user()->currentHousehold; $transaction = $household->transactions()->findOrFail($transactionId); $transaction->delete(); } public function confirmRecurrence(int $recurringId, string $amount): void { $household = auth()->user()->currentHousehold; $recurring = $household->recurringTransactions()->findOrFail($recurringId); $today = now(); if ($recurring->wasGeneratedFor((int) $today->year, (int) $today->month)) { return; } $household->transactions()->create([ 'category_id' => $recurring->category_id, 'recurring_transaction_id' => $recurring->id, 'user_id' => auth()->id(), 'date' => $today->toDateString(), 'date_effective' => $today->toDateString(), 'amount' => $amount, 'details' => $recurring->name, ]); $recurring->update([ 'last_generated_year' => $today->year, 'last_generated_month' => $today->month, ]); $this->dispatch('transaction-added'); } public function dismissRecurrence(int $recurringId): void { $household = auth()->user()->currentHousehold; $recurring = $household->recurringTransactions()->findOrFail($recurringId); $today = now(); $recurring->update([ 'last_generated_year' => $today->year, 'last_generated_month' => $today->month, ]); } /** * Budget planifié pour cette catégorie sur le mois en cours, en résolvant * l'override du mois s'il existe sinon le montant par défaut de l'année. */ private function budgetForCurrentMonth(Category $category, int $year, int $month): ?float { $lines = $category->budgetLines()->where('year', $year)->get(); $override = $lines->firstWhere('month', $month); if ($override) { return (float) $override->amount; } $default = $lines->firstWhere('month', null); return $default ? (float) $default->amount : null; } private function trackedForCategoryThisMonth(Category $category, int $year, int $month): float { return (float) $category->transactions() ->whereYear('date_effective', $year) ->whereMonth('date_effective', $month) ->sum('amount'); } public function render() { $household = auth()->user()->currentHousehold; $transactions = $household->transactions() ->with('category') ->orderByDesc('date') ->orderByDesc('id') ->get(); $chronological = $transactions->sortBy(['date', 'id'])->values(); $running = []; $balance = 0.0; foreach ($chronological as $t) { $sign = $t->category->type === CategoryType::Revenu ? 1 : -1; $balance += (float) $t->amount * $sign; $running[$t->id] = $balance; } $year = (int) now()->year; $month = (int) now()->month; $revenueCategoryIds = $household->categories()->where('type', CategoryType::Revenu)->pluck('id'); $revenueBudget = (float) BudgetLine::whereIn('category_id', $revenueCategoryIds) ->where('year', $year) ->where(fn ($q) => $q->where('month', $month)->orWhereNull('month')) ->get() ->groupBy('category_id') ->map(fn ($lines) => $lines->firstWhere('month', $month)?->amount ?? $lines->firstWhere('month', null)?->amount ?? 0) ->sum(); $revenueTracked = (float) $household->transactions() ->whereHas('category', fn ($q) => $q->where('type', CategoryType::Revenu)) ->whereYear('date_effective', $year) ->whereMonth('date_effective', $month) ->sum('amount'); $unaffected = max(0, $revenueBudget - $revenueTracked); $today = now(); $pendingRecurrences = $household->recurringTransactions() ->with('category') ->where('active', true) ->where('day_of_month', '<=', $today->day) ->get() ->reject(fn (RecurringTransaction $r) => $r->wasGeneratedFor((int) $today->year, (int) $today->month)) ->sortBy('day_of_month') ->values(); $categories = $household->categories()->orderBy('type')->orderBy('position')->get(); $categoryProgress = $categories ->whereIn('type', [CategoryType::Depense, CategoryType::Epargne]) ->mapWithKeys(function (Category $category) use ($year, $month) { $budget = $this->budgetForCurrentMonth($category, $year, $month); if ($budget === null || $budget <= 0) { return [$category->id => null]; } $tracked = $this->trackedForCategoryThisMonth($category, $year, $month); return [$category->id => min(100, round($tracked / $budget * 100))]; }); return view('livewire.suivi.index', [ 'transactions' => $transactions, 'running' => $running, 'categories' => $categories, 'categoryProgress' => $categoryProgress, 'nbrThisYear' => $transactions->filter(fn (Transaction $t) => $t->date->year === $year)->count(), 'nbrTotal' => $transactions->count(), 'lastEntry' => $transactions->first(), 'unaffected' => $unaffected, 'pendingRecurrences' => $pendingRecurrences, ]); } }