Skip to content

Commit

Permalink
Enhance Budget Forecasting and Tracking Capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
sweep-ai[bot] authored Dec 24, 2024
1 parent 288e3e5 commit 01f4c1b
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 11 deletions.
20 changes: 20 additions & 0 deletions app/Filament/Resources/BudgetResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Filament\Tables;
use Filament\Forms\Form;
use Filament\Tables\Table;
use App\Services\BudgetService;

class BudgetResource extends Resource
{
Expand All @@ -32,6 +33,11 @@ public static function form(Form $form): Form
Forms\Components\TextInput::make('planned_amount')
->numeric()
->required(),
Forms\Components\TextInput::make('forecast_amount')
->numeric()
->disabled(),
Forms\Components\Toggle::make('is_approved')
->label('Approved'),
Forms\Components\TextInput::make('description')
->maxLength(255),
]);
Expand All @@ -48,6 +54,12 @@ public static function table(Table $table): Table
->date(),
Tables\Columns\TextColumn::make('planned_amount')
->money(),
Tables\Columns\TextColumn::make('forecast_amount')
->money(),
Tables\Columns\TextColumn::make('variance')
->money(),
Tables\Columns\IconColumn::make('is_approved')
->boolean(),
Tables\Columns\TextColumn::make('description'),
])
->filters([
Expand All @@ -56,6 +68,14 @@ public static function table(Table $table): Table
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
Tables\Actions\Action::make('generate_forecast')
->action(function (Budget $record) {
$budgetService = new BudgetService();
$budgetService->generateForecast($record);
})
->requiresConfirmation()
->color('success')
->icon('heroicon-o-calculator'),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
Expand Down
20 changes: 18 additions & 2 deletions app/Models/Budget.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,33 @@ class Budget extends Model
'start_date',
'end_date',
'planned_amount',
'description'
'description',
'forecast_amount',
'forecast_method',
'is_approved'
];

protected $casts = [
'start_date' => 'date',
'end_date' => 'date',
'planned_amount' => 'decimal:2'
'planned_amount' => 'decimal:2',
'forecast_amount' => 'decimal:2',
'is_approved' => 'boolean'
];

public function account(): BelongsTo
{
return $this->belongsTo(Account::class);
}

public function getVarianceAttribute()
{
return $this->forecast_amount - $this->planned_amount;
}

public function getVariancePercentageAttribute()
{
if ($this->planned_amount == 0) return 0;
return ($this->forecast_amount - $this->planned_amount) / $this->planned_amount * 100;
}
}
56 changes: 47 additions & 9 deletions app/Services/BudgetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,64 @@ public function getBudgetComparison($startDate, $endDate)

return $budgets->map(function ($budget) {
$account = $budget->account;
$actualAmount = $account->debitTransactions()
->whereBetween('transaction_date', [$budget->start_date, $budget->end_date])
->sum('amount') -
$account->creditTransactions()
->whereBetween('transaction_date', [$budget->start_date, $budget->end_date])
->sum('amount');

$actualAmount = $this->calculateActualAmount($account, $budget);
$variance = $actualAmount - $budget->planned_amount;
$percentageUsed = $budget->planned_amount != 0 ?
($actualAmount / $budget->planned_amount) * 100 : 0;
$percentageUsed = $this->calculatePercentageUsed($actualAmount, $budget->planned_amount);

return [
'account_name' => $account->name,
'planned_amount' => $budget->planned_amount,
'actual_amount' => $actualAmount,
'forecast_amount' => $budget->forecast_amount,
'variance' => $variance,
'percentage_used' => round($percentageUsed, 2),
'start_date' => $budget->start_date,
'end_date' => $budget->end_date
];
});
}

public function generateForecast(Budget $budget)
{
$account = $budget->account;
$historicalData = $this->getHistoricalData($account);

// Simple moving average forecast
$forecastAmount = $this->calculateMovingAverage($historicalData);

$budget->forecast_amount = $forecastAmount;
$budget->forecast_method = 'moving_average';
$budget->save();

return $forecastAmount;
}

private function getHistoricalData($account)
{
return $account->transactions()
->select('transaction_date', 'amount')
->orderBy('transaction_date', 'desc')
->limit(12)
->get();
}

private function calculateMovingAverage($historicalData)
{
return $historicalData->avg('amount');
}

private function calculateActualAmount($account, $budget)
{
return $account->debitTransactions()
->whereBetween('transaction_date', [$budget->start_date, $budget->end_date])
->sum('amount') -
$account->creditTransactions()
->whereBetween('transaction_date', [$budget->start_date, $budget->end_date])
->sum('amount');
}

private function calculatePercentageUsed($actualAmount, $plannedAmount)
{
return $plannedAmount != 0 ? ($actualAmount / $plannedAmount) * 100 : 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table('budgets', function (Blueprint $table) {
$table->decimal('forecast_amount', 15, 2)->nullable();
$table->string('forecast_method')->nullable();
$table->boolean('is_approved')->default(false);
});
}

public function down(): void
{
Schema::table('budgets', function (Blueprint $table) {
$table->dropColumn(['forecast_amount', 'forecast_method', 'is_approved']);
});
}
};

0 comments on commit 01f4c1b

Please sign in to comment.