Some fixes for budgets

This commit is contained in:
Sander Dorigo 2014-11-04 20:37:00 +01:00
parent 09e1f68c69
commit aaab7f8e0e
13 changed files with 676 additions and 274 deletions

View File

@ -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');
// }
}

View File

@ -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());

View File

@ -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;
}
/**

View File

@ -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) {

View File

@ -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']);

View File

@ -0,0 +1,21 @@
<form style="display: inline;" action="{{route('budgets.postIncome')}}" method="POST">
{{Form::token()}}
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Update (expected) income for {{$date->format('F Y')}}</h4>
</div>
<div class="modal-body">
<div class="input-group">
<div class="input-group-addon"></div>
<input step="any" class="form-control" id="amount" value="{{$amount->data}}" autocomplete="off" name="amount" type="number">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,150 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="panel panel-default">
<div class="panel-heading">
{{\Session::get('start')->format('F Y')}}
</div>
<div class="panel-body">
<div class="row">
<div class="col-lg-6 col-md-4 col-sm-3">
<small>Budgeted: <span id="budgetedAmount" data-value="300">{{mf(300)}}</span></small>
</div>
<div class="col-lg-6 col-md-4 col-sm-3" style="text-align:right;">
<small>Income {{\Session::get('start')->format('F Y')}}:
<a href="#" class="updateIncome"><span id="totalAmount" data-value="{{$budgetAmount->data}}">{{mf($budgetAmount->data)}}</span></a></small>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="progress">
<div class="progress-bar" id="progress-bar-default" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
<div class="progress-bar progress-bar-danger" id="progress-bar-danger" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
<div class="progress-bar progress-bar-warning" id="progress-bar-warning" role="progressbar" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
</div>
</div>
</div>
</div>
</div>
<h3></h3>
</div>
<div class="col-lg-3 col-sm-4 col-md-4">
<!-- time based navigation -->
@include('partials.date_nav')
</div>
</div>
<div class="row">
@foreach($budgets as $budget)
<div class="col-lg-3 col-sm-4 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
@if($budget->currentRep)
<a href="{{route('budgets.show',[$budget->id,$budget->currentRep->id])}}" id="budget-link-{{$budget->id}}">{{{$budget->name}}}</a>
@else
<a href="{{route('budgets.show',$budget->id)}}" id="budget-link-{{$budget->id}}">{{{$budget->name}}}</a>
@endif
</div>
<div class="panel-body">
<!-- the range in which the budget can be set -->
<p>
@if($budget->currentRep)
<input type="range" data-id="{{$budget->id}}" data-spent="{{$budget->spent}}" id="budget-range-{{$budget->id}}" max="900" min="0" value="{{$budget->currentRep->amount}}" />
@else
<input type="range" data-id="{{$budget->id}}" data-spent="{{$budget->spent}}" id="budget-range-{{$budget->id}}" max="900" min="0" value="0" />
@endif
</p>
<!-- some textual info about the budget. Updates dynamically. -->
<p>
<!-- budget-holder-X holds all the elements -->
<span id="budget-holder-{{$budget->id}}">
@if($budget->currentRep)
<!-- budget-description-X holds the description. -->
<span id="budget-description-{{$budget->id}}">Budgeted: </span>
<!-- budget-info-X holds the input and the euro-sign: -->
<span id="budget-info-{{$budget->id}}">
@if($budget->limit > $budget->spent)
<span class="text-success">&euro;</span> <input type="number" min="0" max="900" data-id="{{$budget->id}}" step="1" value="{{$budget->limit}}" style="width:50px;color:#3c763d;" />
@else
<span class="text-danger">&euro;</span> <input type="number" min="0" max="900" data-id="{{$budget->id}}" step="1" value="{{$budget->limit}}" style="width:50px;color:#a94442;" />
@endif
</span>
@else
<span id="budget-description-{{$budget->id}}"><em>No budget</em></span>
<span id="budget-info-{{$budget->id}}">
<span class="text-success" style="display:none;">&euro;</span> <input style="display:none;" data-id="{{$budget->id}}" type="number" min="0" max="900" step="1" value="0" style="width:50px;color:#3c763d;" />
</span>
@endif
</span>
</p>
<p>
<span id="spent-{{$budget->id}}" data-value="{{$budget->spent}}">Spent: {{mf($budget->spent)}}</span>
</p>
</div>
</div>
</div>
@endforeach
</div>
@foreach($budgets as $budget)
{{--
<div class="row">
<div class="col-lg-9 col-sm-8 col-md-8">
<div class="row">
<div class="col-lg-3 col-md-4 col-sm-4">
{{$budget->name}}
</div>
<div class="col-lg-7 col-md-4 col-sm-4">
</div>
<div class="col-lg-2 col-md-4 col-sm-3">
<span id="budget-range-display-{{$budget->id}}" data-id="{{$budget->id}}"></span>
</div>
</div>
<div class="row">
<div class="col-lg-7 col-lg-offset-3 col-md-4 col-md-offset-4 col-sm-4 col-sm-offset-4">
@if($budget->pct > 0)
<!-- display a progress bar. -->
<div class="progress" id="budget-progress-{{$budget->id}}" data-spent="{{$budget->spent}}" data-amount="{{$budget->limit}}">
</div>
@else
<!-- do NOT display a progress bar. -->
<div style="display:none;" class="progress" id="budget-progress-{{$budget->id}}" data-spent="{{$budget->spent}}" data-amount="{{$budget->limit}}">
</div>
@endif
<!--
@if($budget->currentRep)
@if($budget->currentRep->amount <= $budget->spent)
Overspent on budget (budgeted: {{$budget->currentRep->amount}}, spent: {{$budget->spent}}).
@else
NOT overspent on budget (budgeted: {{$budget->currentRep->amount}}, spent: {{$budget->spent}}).
@endif
@else
No limit.
@endif
-->
</div>
</div>
</div>
</div>
--}}
@endforeach
<!-- DIALOG -->
<div class="modal fade" id="monthlyBudgetModal">
</div><!-- /.modal -->
@stop
@section('scripts')
{{HTML::script('assets/javascript/firefly/budgets.js')}}
@stop

View File

@ -44,7 +44,7 @@
<!-- BUDGETS -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index.date')}}">Budgets and spending</a>
<i class="fa fa-tasks fa-fw"></i> <a href="{{route('budgets.index')}}">Budgets and spending</a>
</div>
<div class="panel-body">
<div id="budgets-chart"></div>

View File

@ -31,9 +31,9 @@
<div class="col-sm-8">
<input type="submit" name="submit" value="Remove envelope" class="btn btn-danger" />
@if(Input::get('from') == 'date')
<a href="{{route('budgets.index.date')}}" class="btn-default btn">Cancel</a>
<a href="{{route('budgets.index')}}" class="btn-default btn">Cancel</a>
@else
<a href="{{route('budgets.index.budget')}}" class="btn-default btn">Cancel</a>
<a href="{{route('budgets.index')}}" class="btn-default btn">Cancel</a>
@endif
</div>
</div>

View File

@ -48,7 +48,7 @@
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i>
<i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="{{route('profile')}}"><i class="fa fa-user fa-fw"></i> {{Auth::user()->email}}</a>
@ -109,23 +109,8 @@
</ul>
<!-- /.nav-second-level -->
</li>
<li
@if(!(strpos($r,'budgets') === false))
class="active"
@endif
>
<a href="#"><i class="fa fa-tasks fa-fw"></i> Budgets <span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li>
<a @if($r == 'budgets.index.date') class="active" @endif href="{{route('budgets.index.date')}}"><i class="fa fa-calendar fa-fw"></i> Grouped by date</a>
</li>
<li>
<a @if($r == 'budgets.index.budget') class="active" @endif href="{{route('budgets.index.budget')}}"><i class="fa fa-folder-open fa-fw"></i> Grouped by budget</a>
</li>
</ul>
<li>
<a href="{{route('budgets.index')}}"><i class="fa fa-tasks fa-fw"></i> Budgets</a>
</li>
<li>
<a
@ -214,7 +199,7 @@
</li>
-->
<li>
<a href="{{route('budgets.create')}}"><i class="fa fa-tasks fa-fw"></i> Budget</a>
<a href="#"><i class="fa fa-tasks fa-fw"></i> Budget</a>
</li>
<li>
<a href="#"><i class="fa fa-bar-chart fa-fw"></i> Category</a>

View File

@ -104,21 +104,6 @@
<!-- this is the modal for the add/remove money routine: -->
<div class="modal fade" id="moneyManagementModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body&hellip;</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
@stop

View File

@ -0,0 +1,164 @@
$(function () {
updateRanges();
$('input[type="range"]').change(updateSingleRange);
$('input[type="range"]').on('input', updateSingleRange);
$('input[type="number"]').on('change',updateSingleRange);
$('input[type="number"]').on('input',updateSingleRange);
$('.updateIncome').on('click', updateIncome);
});
function updateSingleRange(e) {
// get some values:
var input = $(e.target);
var id = input.data('id');
var value = parseInt(input.val());
var spent = parseFloat($('#spent-' + id).data('value'));
console.log('Spent vs budgeted: ' + spent + ' vs ' + value)
// update small display:
if(value > 0) {
// show the input:
$('#budget-info-' + id +' span').show();
$('#budget-info-' + id +' input').show();
// update the text:
$('#budget-description-' + id).text('Budgeted: ');
//if(value < spent) {
// $('#budgeted-' + id).html('Budgeted: <span class="text-danger">\u20AC ' + value.toFixed(2) + '</span>');
//} else {
// $('#budgeted-' + id).html('Budgeted: <span class="text-success">\u20AC ' + value.toFixed(2) + '</span>');
//}
} else {
console.log('Set to zero!');
// hide the input:
$('#budget-info-' + id +' span').hide();
$('#budget-info-' + id +' input').hide();
// update the text:
$('#budget-description-' + id).html('<em>No budget</em>');
//$('#budgeted-' + id).html('<em>No budget</em>');
}
// update the range display text:
$('#budget-range-display-' + id).text('\u20AC ' + value.toFixed(2));
// send a post to Firefly to update the amount:
console.log('Value is: ' + value);
$.post('budgets/amount/' + id, {amount: value}).success(function (data) {
console.log('Budget ' + data.name + ' updated!');
// update the link if relevant:
$('#budget-link-' + id).attr('href','budgets/show/' + id + '/' + data.repetition);
});
if(input.attr('type') == 'number') {
// update the range-input:
$('#budget-range-' + id).val(value);
} else {
// update the number-input:
$('#budget-info-' + id +' input').val(value);
}
// update or hide the bar, whichever is necessary.
updateTotal();
return value;
}
function updateTotal() {
var sum = 0;
$('input[type="range"]').each(function (i, v) {
// get some values:
sum += parseInt($(v).val());
});
/**
* Update total sum:
*/
var totalAmount = parseInt($('#totalAmount').data('value'));
if (sum <= totalAmount) {
var pct = sum / totalAmount * 100;
$('#progress-bar-default').css('width', pct + '%');
$('#progress-bar-warning').css('width', '0');
$('#progress-bar-danger').css('width', '0');
$('#budgetedAmount').text('\u20AC ' + sum.toFixed(2)).addClass('text-success').removeClass('text-danger');
} else {
// we gaan er X overheen,
var pct = totalAmount / sum * 100;
console.log(pct)
var danger = 100 - pct;
var err = 100 - danger;
$('#progress-bar-default').css('width', 0);
$('#progress-bar-warning').css('width', err + '%');
$('#progress-bar-danger').css('width', danger + '%');
$('#budgetedAmount').text('\u20AC ' + sum.toFixed(2)).addClass('text-danger').removeClass('text-success');
}
}
function updateIncome(e) {
$('#monthlyBudgetModal').empty().load('budgets/income').modal('show');
return false;
}
function updateRanges() {
/**
* Update all ranges.
*/
var sum = 0;
$('input[type="range"]').each(function (i, v) {
// get some values:
var input = $(v);
var id = input.data('id');
var value = parseInt(input.val());
// calculate sum:
sum += value
// update small display:
$('#budget-range-display-' + id).text('\u20AC ' + value.toFixed(2));
// update progress bar (if relevant)
var barHolder = $('#budget-progress-' + id);
var spent = parseFloat(barHolder.data('spent'));
if (value > 0 && spent > 0) {
console.log('Add bar')
//barHolder.append($('<div class="progress-bar" id="progress-bar-something-' + id + '" role="progressbar" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100" style="width: 10%;"></div>'));
}
// send a post to Firefly to update the amount:
$.post('budgets/amount/' + id, {amount: value}).success(function (data) {
console.log('Budget ' + data.name + ' updated!');
});
});
/**
* Update total sum:
*/
var totalAmount = parseInt($('#totalAmount').data('value'));
if (sum <= totalAmount) {
var pct = sum / totalAmount * 100;
$('#progress-bar-default').css('width', pct + '%');
$('#progress-bar-warning').css('width', '0');
$('#progress-bar-danger').css('width', '0');
$('#budgetedAmount').text('\u20AC ' + sum.toFixed(2)).addClass('text-success').removeClass('text-danger');
} else {
// we gaan er X overheen,
var pct = totalAmount / sum * 100;
console.log(pct)
var danger = 100 - pct;
var err = 100 - danger;
$('#progress-bar-default').css('width', 0);
$('#progress-bar-warning').css('width', err + '%');
$('#progress-bar-danger').css('width', danger + '%');
$('#budgetedAmount').text('\u20AC ' + sum.toFixed(2)).addClass('text-danger').removeClass('text-success');
}
}

View File

@ -11,7 +11,6 @@ function addMoney(e) {
}
function removeMoney(e) {
var pigID = parseInt($(e.target).data('id'));
var pigID = parseInt($(e.target).data('id'));
$('#moneyManagementModal').empty().load('piggybanks/remove/' + pigID).modal('show');