diff --git a/app/controllers/BudgetController.php b/app/controllers/BudgetController.php index 6ed90c18f5..21aed96411 100644 --- a/app/controllers/BudgetController.php +++ b/app/controllers/BudgetController.php @@ -4,248 +4,325 @@ use Carbon\Carbon; use Firefly\Exception\FireflyException; use Firefly\Helper\Controllers\BudgetInterface as BI; use Firefly\Storage\Budget\BudgetRepositoryInterface as BRI; +use FireflyIII\Exception\NotImplementedException; + -/** - * Class BudgetController - * - * @SuppressWarnings(PHPMD.CamelCasePropertyName) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * - */ class BudgetController extends BaseController { - protected $_budgets; - protected $_repository; - /** - * @param BI $budgets - * @param BRI $repository - */ - public function __construct(BI $budgets, BRI $repository) + public function __construct() { - $this->_budgets = $budgets; - $this->_repository = $repository; View::share('title', 'Budgets'); View::share('mainTitleIcon', 'fa-tasks'); } - public function nobudget($view = 'session') { - switch($view) { - default: - throw new FireflyException('Cannot show transactions without a budget for view "'.$view.'".'); - break; - case 'session': - $start = Session::get('start'); - $end = Session::get('end'); - break; - } - - // Add expenses that have no budget: - $set = \Auth::user()->transactionjournals()->whereNotIn( - 'transaction_journals.id', function ($query) use ($start, $end) { - $query->select('transaction_journals.id')->from('transaction_journals') - ->leftJoin( - 'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', - 'transaction_journals.id' - ) - ->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where('components.class', 'Budget'); - } - )->before($end)->after($start)->get(); - - return View::make('budgets.nobudget') - ->with('view', $view) - ->with('transactions',$set) - ->with('subTitle', 'Transactions without a budget'); - } - - /** - * @return $this|\Illuminate\View\View - */ - public function create() + public function postUpdateIncome() { - $periods = \Config::get('firefly.periods_to_text'); + /** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */ + $preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); + $date = Session::get('start'); - return View::make('budgets.create')->with('periods', $periods)->with('subTitle', 'Create a new budget'); + $value = intval(Input::get('amount')); + $preferences->set('budgetIncomeTotal' . $date->format('FY'), $value); + return Redirect::route('budgets.index'); } /** + * Update the amount for a budget's limitrepetition and/or create it. + * * @param Budget $budget - * - * @return $this */ - public function delete(Budget $budget) + public function amount(Budget $budget) { - return View::make('budgets.delete')->with('budget', $budget) - ->with('subTitle', 'Delete budget "' . $budget->name . '"'); - } + $amount = intval(Input::get('amount')); + $date = Session::get('start'); + /** @var \Limit $limit */ + $limit = $budget->limits()->where('startdate', $date->format('Y-m-d'))->first(); + if (!$limit) { + // create one! + $limit = new Limit; + $limit->budget()->associate($budget); + $limit->startdate = $date; + $limit->amount = $amount; + $limit->repeat_freq = 'monthly'; + $limit->repeats = 0; + $limit->save(); + Event::fire('limits.store', [$limit]); - /** - * @param Budget $budget - * - * @return \Illuminate\Http\RedirectResponse - */ - public function destroy(Budget $budget) - { - // remove budget - Event::fire('budgets.destroy', [$budget]); // just before deletion. - $this->_repository->destroy($budget); - Session::flash('success', 'The budget was deleted.'); - - // redirect: - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } - return Redirect::route('budgets.index.budget'); - - } - - /** - * @param Budget $budget - * - * @return $this - */ - public function edit(Budget $budget) - { - return View::make('budgets.edit')->with('budget', $budget) - ->with('subTitle', 'Edit budget "' . $budget->name . '"'); - - } - - /** - * @return $this|\Illuminate\View\View - */ - public function indexByBudget() - { - View::share('subTitleIcon', 'fa-folder-open'); - - $budgets = $this->_repository->get(); - - return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon) - ->with('subTitle', 'Grouped by budget'); - - } - - /** - * @return $this - */ - public function indexByDate() - { - View::share('subTitleIcon', 'fa-calendar'); - - // get a list of dates by getting all repetitions: - $set = $this->_repository->get(); - $budgets = $this->_budgets->organizeByDate($set); - - return View::make('budgets.indexByDate')->with('budgets', $budgets) - ->with('subTitle', 'Grouped by date'); - - - } - - /** - * Three use cases for this view: - * - * - Show everything. - * - Show a specific repetition. - * - Show everything shows NO repetition. - * - * @param Budget $budget - * @param LimitRepetition $repetition - * - * @return int - */ - public function show(Budget $budget, \LimitRepetition $repetition = null) - { - $useSessionDates = Input::get('useSession') == 'true' ? true : false; - $view = null; - $title = null; - \Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true')); - switch (true) { - case (!is_null($repetition)): - $data = $this->_budgets->organizeRepetition($repetition); - $view = 1; - $title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf( - $repetition->limit->amount, - false - ); - break; - case (Input::get('noenvelope') == 'true'): - $data = $this->_budgets->outsideRepetitions($budget); - $view = 2; - $title = $budget->name . ', transactions outside an envelope'; - break; - default: - $data = $this->_budgets->organizeRepetitions($budget, $useSessionDates); - $view = $useSessionDates ? 3 : 4; - $title = $useSessionDates ? $budget->name . ' in session period' : $budget->name; - break; - } - - return View::make('budgets.show') - ->with('budget', $budget) - ->with('repetitions', $data) - ->with('view', $view) - ->with('highlight', Input::get('highlight')) - ->with('useSessionDates', $useSessionDates) - ->with('subTitle', 'Overview for ' . $title); - } - - /** - * @return \Illuminate\Http\RedirectResponse - */ - public function store() - { - - $budget = $this->_repository->store(Input::all()); - if ($budget->validate()) { - Event::fire('budgets.store', [$budget]); - Session::flash('success', 'Budget created!'); - - if (Input::get('create') == '1') { - return Redirect::route('budgets.create', ['from' => Input::get('from')]); - } - - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } else { - return Redirect::route('budgets.index.budget'); - } } else { - Session::flash('error', 'Could not save the new budget'); - - return Redirect::route('budgets.create')->withInput()->withErrors($budget->errors()); + $limit->amount = $amount; + $limit->save(); + Event::fire('limits.update', [$limit]); } - - } - - /** - * @param Budget $budget - * - * @return $this|\Illuminate\Http\RedirectResponse - */ - public function update(Budget $budget) - { - $budget = $this->_repository->update($budget, Input::all()); - if ($budget->validate()) { - Event::fire('budgets.update', [$budget]); - Session::flash('success', 'Budget "' . $budget->name . '" updated.'); - - if (Input::get('from') == 'date') { - return Redirect::route('budgets.index'); - } else { - return Redirect::route('budgets.index.budget'); - } + // try to find the limit repetition for this limit: + $repetition = $limit->limitrepetitions()->first(); + if($repetition) { + return Response::json(['name' => $budget->name,'repetition' => $repetition->id]); } else { - Session::flash('error', 'Could not update budget: ' . $budget->errors()->first()); - - return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($budget->errors()); + return Response::json(['name' => $budget->name,'repetition' => null]); } } + public function index() + { + + /** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */ + $preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); + $date = Session::get('start'); + + /** @var \FireflyIII\Database\Budget $repos */ + $repos = App::make('FireflyIII\Database\Budget'); + $budgets = $repos->get(); + + // get the limits for the current month. + $date = \Session::get('start'); + /** @var \Budget $budget */ + foreach ($budgets as $budget) { + + $budget->spent = $repos->spentInMonth($budget, $date); + $budget->pct = 0; + $budget->limit = 0; + + /** @var \Limit $limit */ + foreach ($budget->limits as $limit) { + /** @var \LimitRepetition $repetition */ + foreach ($limit->limitrepetitions as $repetition) { + if ($repetition->startdate == $date) { + $budget->currentRep = $repetition; + $budget->limit = floatval($repetition->amount); + if($budget->limit > $budget->spent) { + // not overspent: + $budget->pct = 30; + } else { + $budget->pct = 50; + } + + } + } + } + } + + $budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000); + + return View::make('budgets.index', compact('budgets'))->with('budgetAmount', $budgetAmount); + } + + public function updateIncome() + { + $date = Session::get('start'); + /** @var \Firefly\Helper\Preferences\PreferencesHelperInterface $preferences */ + $preferences = App::make('Firefly\Helper\Preferences\PreferencesHelperInterface'); + $budgetAmount = $preferences->get('budgetIncomeTotal' . $date->format('FY'), 1000); + return View::make('budgets.income')->with('amount', $budgetAmount)->with('date', $date); + } + + + + +// +// public function create() +// { +// throw new NotImplementedException; +//// $periods = \Config::get('firefly.periods_to_text'); +//// +//// return View::make('budgets.create')->with('periods', $periods)->with('subTitle', 'Create a new budget'); +// } +// +// public function delete(Budget $budget) +// { +// throw new NotImplementedException; +//// return View::make('budgets.delete')->with('budget', $budget) +//// ->with('subTitle', 'Delete budget "' . $budget->name . '"'); +// } +// +// public function destroy(Budget $budget) +// { +// throw new NotImplementedException; +//// // remove budget +//// Event::fire('budgets.destroy', [$budget]); // just before deletion. +//// $this->_repository->destroy($budget); +//// Session::flash('success', 'The budget was deleted.'); +//// +//// // redirect: +//// if (Input::get('from') == 'date') { +//// return Redirect::route('budgets.index'); +//// } +//// return Redirect::route('budgets.index.budget'); +// +// } +// public function edit(Budget $budget) +// { +// throw new NotImplementedException; +//// return View::make('budgets.edit')->with('budget', $budget) +//// ->with('subTitle', 'Edit budget "' . $budget->name . '"'); +// +// } + +// /** +// * @return $this|\Illuminate\View\View +// */ +// public function indexByBudget() +// { +// View::share('subTitleIcon', 'fa-folder-open'); +// +// $budgets = $this->_repository->get(); +// +// return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', new Carbon) +// ->with('subTitle', 'Grouped by budget'); +// +// } +// +// /** +// * @return $this +// */ +// public function indexByDate() +// { +// View::share('subTitleIcon', 'fa-calendar'); +// +// // get a list of dates by getting all repetitions: +// $set = $this->_repository->get(); +// $budgets = $this->_budgets->organizeByDate($set); +// +// return View::make('budgets.indexByDate')->with('budgets', $budgets) +// ->with('subTitle', 'Grouped by date'); +// +// +// } +// +// /** +// * Three use cases for this view: +// * +// * - Show everything. +// * - Show a specific repetition. +// * - Show everything shows NO repetition. +// * +// * @param Budget $budget +// * @param LimitRepetition $repetition +// * +// * @return int +// */ +// public function show(Budget $budget, \LimitRepetition $repetition = null) +// { +// $useSessionDates = Input::get('useSession') == 'true' ? true : false; +// $view = null; +// $title = null; +// \Log::debug('Is envelope true? ' . (Input::get('noenvelope') == 'true')); +// switch (true) { +// case (!is_null($repetition)): +// $data = $this->_budgets->organizeRepetition($repetition); +// $view = 1; +// $title = $budget->name . ', ' . $repetition->periodShow() . ', ' . mf( +// $repetition->limit->amount, +// false +// ); +// break; +// case (Input::get('noenvelope') == 'true'): +// $data = $this->_budgets->outsideRepetitions($budget); +// $view = 2; +// $title = $budget->name . ', transactions outside an envelope'; +// break; +// default: +// $data = $this->_budgets->organizeRepetitions($budget, $useSessionDates); +// $view = $useSessionDates ? 3 : 4; +// $title = $useSessionDates ? $budget->name . ' in session period' : $budget->name; +// break; +// } +// +// return View::make('budgets.show') +// ->with('budget', $budget) +// ->with('repetitions', $data) +// ->with('view', $view) +// ->with('highlight', Input::get('highlight')) +// ->with('useSessionDates', $useSessionDates) +// ->with('subTitle', 'Overview for ' . $title); +// } +// +// /** +// * @return \Illuminate\Http\RedirectResponse +// */ +// public function store() +// { +// +// $budget = $this->_repository->store(Input::all()); +// if ($budget->validate()) { +// Event::fire('budgets.store', [$budget]); +// Session::flash('success', 'Budget created!'); +// +// if (Input::get('create') == '1') { +// return Redirect::route('budgets.create', ['from' => Input::get('from')]); +// } +// +// if (Input::get('from') == 'date') { +// return Redirect::route('budgets.index'); +// } else { +// return Redirect::route('budgets.index.budget'); +// } +// } else { +// Session::flash('error', 'Could not save the new budget'); +// +// return Redirect::route('budgets.create')->withInput()->withErrors($budget->errors()); +// } +// +// } +// +// /** +// * @param Budget $budget +// * +// * @return $this|\Illuminate\Http\RedirectResponse +// */ +// public function update(Budget $budget) +// { +// $budget = $this->_repository->update($budget, Input::all()); +// if ($budget->validate()) { +// Event::fire('budgets.update', [$budget]); +// Session::flash('success', 'Budget "' . $budget->name . '" updated.'); +// +// if (Input::get('from') == 'date') { +// return Redirect::route('budgets.index'); +// } else { +// return Redirect::route('budgets.index.budget'); +// } +// } else { +// Session::flash('error', 'Could not update budget: ' . $budget->errors()->first()); +// +// return Redirect::route('budgets.edit', $budget->id)->withInput()->withErrors($budget->errors()); +// } +// +// } + +// public function nobudget($view = 'session') { +// switch($view) { +// default: +// throw new FireflyException('Cannot show transactions without a budget for view "'.$view.'".'); +// break; +// case 'session': +// $start = Session::get('start'); +// $end = Session::get('end'); +// break; +// } +// +// // Add expenses that have no budget: +// $set = \Auth::user()->transactionjournals()->whereNotIn( +// 'transaction_journals.id', function ($query) use ($start, $end) { +// $query->select('transaction_journals.id')->from('transaction_journals') +// ->leftJoin( +// 'component_transaction_journal', 'component_transaction_journal.transaction_journal_id', '=', +// 'transaction_journals.id' +// ) +// ->leftJoin('components', 'components.id', '=', 'component_transaction_journal.component_id') +// ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) +// ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) +// ->where('components.class', 'Budget'); +// } +// )->before($end)->after($start)->get(); +// +// return View::make('budgets.nobudget') +// ->with('view', $view) +// ->with('transactions',$set) +// ->with('subTitle', 'Transactions without a budget'); +// } + } \ No newline at end of file diff --git a/app/controllers/GoogleChartController.php b/app/controllers/GoogleChartController.php index 4c72298b1c..571ec2fb69 100644 --- a/app/controllers/GoogleChartController.php +++ b/app/controllers/GoogleChartController.php @@ -98,6 +98,7 @@ class GoogleChartController extends BaseController return Response::json($chart->getData()); } + /** * @param $year * @@ -112,7 +113,7 @@ class GoogleChartController extends BaseController } /** @var \Grumpydictator\Gchart\GChart $chart */ $chart = App::make('gchart'); - $chart->addColumn('Month', 'string'); + $chart->addColumn('Summary', 'string'); $chart->addColumn('Income', 'number'); $chart->addColumn('Expenses', 'number'); @@ -121,12 +122,12 @@ class GoogleChartController extends BaseController $end = clone $start; $end->endOfYear(); - $income = 0; + $income = 0; $expense = 0; while ($start < $end) { // total income: - $income += $tj->getSumOfIncomesByMonth($start); + $income += $tj->getSumOfIncomesByMonth($start); $expense += $tj->getSumOfExpensesByMonth($start); $start->addMonth(); @@ -134,7 +135,6 @@ class GoogleChartController extends BaseController $chart->addRow('Sum', $income, $expense); - $chart->generate(); return Response::json($chart->getData()); diff --git a/app/lib/FireflyIII/Database/Budget.php b/app/lib/FireflyIII/Database/Budget.php index 86d89d65c9..867237b802 100644 --- a/app/lib/FireflyIII/Database/Budget.php +++ b/app/lib/FireflyIII/Database/Budget.php @@ -106,7 +106,22 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface */ public function get() { - return $this->getUser()->budgets()->get(); + $budgets = $this->getUser()->budgets()->get(); + + return $budgets; + } + + /** + * @param \Budget $budget + * @param Carbon $date + * @return float + */ + public function spentInMonth(\Budget $budget, Carbon $date) { + $end = clone $date; + $date->startOfMonth(); + $end->endOfMonth(); + $sum = floatval($budget->transactionjournals()->before($end)->after($date)->lessThan(0)->sum('amount')) * -1; + return $sum; } /** diff --git a/app/models/Limit.php b/app/models/Limit.php index 5c11896933..0255c0429e 100644 --- a/app/models/Limit.php +++ b/app/models/Limit.php @@ -89,9 +89,9 @@ class Limit extends Ardent break; } $end->subDay(); - $count = $this->limitrepetitions()->where('startdate', $start->format('Y-m-d'))->where( - 'enddate', $start->format('Y-m-d') - )->count(); + $count = $this->limitrepetitions()->where('startdate', $start->format('Y-m-d'))->where('enddate', $end->format('Y-m-d'))->count(); + \Log::debug('All: '.$this->limitrepetitions()->count().' (#'.$this->id.')'); + \Log::debug('Found ' . $count.' limit-reps for limit #' . $this->id.' with start '.$start->format('Y-m-d') .' and end ' . $end->format('Y-m-d')); if ($count == 0) { diff --git a/app/routes.php b/app/routes.php index f9e515d144..dc13af8700 100644 --- a/app/routes.php +++ b/app/routes.php @@ -138,15 +138,17 @@ Route::group( Route::get('/accounts/show/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']); // budget controller: - Route::get('/budgets/date', ['uses' => 'BudgetController@indexByDate', 'as' => 'budgets.index.date']); - Route::get('/budgets/budget', ['uses' => 'BudgetController@indexByBudget', 'as' => 'budgets.index.budget']); - Route::get('/budgets/create', ['uses' => 'BudgetController@create', 'as' => 'budgets.create']); - - Route::get('/budgets/nobudget/{period}', ['uses' => 'BudgetController@nobudget', 'as' => 'budgets.nobudget']); - + Route::get('/budgets', ['uses' => 'BudgetController@index', 'as' => 'budgets.index']); + Route::get('/budgets/income', ['uses' => 'BudgetController@updateIncome', 'as' => 'budgets.income']); Route::get('/budgets/show/{budget}/{limitrepetition?}', ['uses' => 'BudgetController@show', 'as' => 'budgets.show']); - Route::get('/budgets/edit/{budget}', ['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']); - Route::get('/budgets/delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']); + + #Route::get('/budgets/date', ['uses' => 'BudgetController@indexByDate', 'as' => 'budgets.index.date']); + #Route::get('/budgets/budget', ['uses' => 'BudgetController@indexByBudget', 'as' => 'budgets.index.budget']); + #Route::get('/budgets/create', ['uses' => 'BudgetController@create', 'as' => 'budgets.create']); + #Route::get('/budgets/nobudget/{period}', ['uses' => 'BudgetController@nobudget', 'as' => 'budgets.nobudget']); + + #Route::get('/budgets/edit/{budget}', ['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']); + #Route::get('/budgets/delete/{budget}', ['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']); // category controller: Route::get('/categories', ['uses' => 'CategoryController@index', 'as' => 'categories.index']); @@ -258,6 +260,9 @@ Route::group( // user controller Route::get('/logout', ['uses' => 'UserController@logout', 'as' => 'logout']); + Route::post('budgets/amount/{budget}',['uses' => 'BudgetController@amount']); + + } ); @@ -270,9 +275,10 @@ Route::group( Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']); // budget controller: - Route::post('/budgets/store', ['uses' => 'BudgetController@store', 'as' => 'budgets.store']); - Route::post('/budgets/update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'budgets.update']); - Route::post('/budgets/destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'budgets.destroy']); + Route::post('/budgets/income', ['uses' => 'BudgetController@postUpdateIncome', 'as' => 'budgets.postIncome']); + #Route::post('/budgets/store', ['uses' => 'BudgetController@store', 'as' => 'budgets.store']); + #Route::post('/budgets/update/{budget}', ['uses' => 'BudgetController@update', 'as' => 'budgets.update']); + #Route::post('/budgets/destroy/{budget}', ['uses' => 'BudgetController@destroy', 'as' => 'budgets.destroy']); // category controller Route::post('/categories/store', ['uses' => 'CategoryController@store', 'as' => 'categories.store']); diff --git a/app/views/budgets/income.blade.php b/app/views/budgets/income.blade.php new file mode 100644 index 0000000000..4c6e3761df --- /dev/null +++ b/app/views/budgets/income.blade.php @@ -0,0 +1,21 @@ +
\ No newline at end of file diff --git a/app/views/budgets/index.blade.php b/app/views/budgets/index.blade.php new file mode 100644 index 0000000000..bdc4c703e1 --- /dev/null +++ b/app/views/budgets/index.blade.php @@ -0,0 +1,150 @@ +@extends('layouts.default') +@section('content') ++ @if($budget->currentRep) + + @else + + @endif +
+ ++ + + @if($budget->currentRep) + + Budgeted: + + + @if($budget->limit > $budget->spent) + € + @else + € + @endif + + @else + No budget + + + + @endif + +
++ Spent: {{mf($budget->spent)}} +
+