Fixed some bugs in various controllers and started rebuilding the category controller.

This commit is contained in:
Sander Dorigo 2014-11-10 19:03:03 +01:00
parent cb08df0770
commit af9473c126
13 changed files with 291 additions and 220 deletions

View File

@ -312,7 +312,7 @@ class AccountController extends BaseController
default: default:
throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"'); throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
break; break;
case 'create_another': case 'return_to_edit':
case 'store': case 'store':
$messages = $acct->validate($data); $messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */ /** @var MessageBag $messages ['errors'] */

View File

@ -263,7 +263,7 @@ class BudgetController extends BaseController
default: default:
throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"'); throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break; break;
case 'create_another': case 'return_to_edit':
case 'update': case 'update':
$messages = $repos->validate($data); $messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */ /** @var MessageBag $messages ['errors'] */
@ -277,7 +277,7 @@ class BudgetController extends BaseController
$repos->update($budget, $data); $repos->update($budget, $data);
Session::flash('success', 'Budget updated!'); Session::flash('success', 'Budget updated!');
if ($data['post_submit_action'] == 'create_another') { if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('budgets.edit', $budget->id); return Redirect::route('budgets.edit', $budget->id);
} else { } else {
return Redirect::route('budgets.index'); return Redirect::route('budgets.index');

View File

@ -1,26 +1,17 @@
<?php <?php
use Firefly\Exception\FireflyException;
use Firefly\Helper\Controllers\CategoryInterface as CI; use Illuminate\Support\MessageBag;
use Firefly\Storage\Category\CategoryRepositoryInterface as CRI;
/** /**
* Class CategoryController * Class CategoryController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/ */
class CategoryController extends BaseController class CategoryController extends BaseController
{ {
protected $_repository;
protected $_category;
/** /**
* @param CRI $repository *
* @param CI $category
*/ */
public function __construct(CRI $repository, CI $category) public function __construct()
{ {
$this->_repository = $repository;
$this->_category = $category;
View::share('title', 'Categories'); View::share('title', 'Categories');
View::share('mainTitleIcon', 'fa-bar-chart'); View::share('mainTitleIcon', 'fa-bar-chart');
} }
@ -40,8 +31,7 @@ class CategoryController extends BaseController
*/ */
public function delete(Category $category) public function delete(Category $category)
{ {
return View::make('categories.delete')->with('category', $category) return View::make('categories.delete')->with('category', $category)->with('subTitle', 'Delete category "' . $category->name . '"');
->with('subTitle', 'Delete category "' . $category->name . '"');
} }
/** /**
@ -51,7 +41,10 @@ class CategoryController extends BaseController
*/ */
public function destroy(Category $category) public function destroy(Category $category)
{ {
$this->_repository->destroy($category); /** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
$repos->destroy($category);
Session::flash('success', 'The category was deleted.'); Session::flash('success', 'The category was deleted.');
return Redirect::route('categories.index'); return Redirect::route('categories.index');
} }
@ -72,10 +65,7 @@ class CategoryController extends BaseController
*/ */
public function index() public function index()
{ {
$categories = $this->_repository->get(); return View::make('categories.index');
return View::make('categories.index')->with('categories', $categories)
->with('subTitle', 'All your categories');
} }
/** /**
@ -124,15 +114,40 @@ class CategoryController extends BaseController
*/ */
public function update(Category $category) public function update(Category $category)
{ {
$category = $this->_repository->update($category, Input::all()); /** @var \FireflyIII\Database\Category $repos */
if ($category->validate()) { $repos = App::make('FireflyIII\Database\Category');
Session::flash('success', 'Category "' . $category->name . '" updated.'); $data = Input::except('_token');
return Redirect::route('categories.index'); switch (Input::get('post_submit_action')) {
} else { default:
Session::flash('success', 'Could not update category "' . $category->name . '".'); throw new FireflyException('Cannot handle post_submit_action "' . e(Input::get('post_submit_action')) . '"');
break;
case 'return_to_edit':
case 'update':
$messages = $repos->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save category: ' . $messages['errors']->first());
return Redirect::route('categories.edit', $category->id)->withInput()->withErrors($messages['errors']);
}
// store!
$repos->update($category, $data);
Session::flash('success', 'Category updated!');
return Redirect::route('categories.edit')->withErrors($category->errors())->withInput(); if ($data['post_submit_action'] == 'return_to_edit') {
return Redirect::route('categories.edit', $category->id);
} else {
return Redirect::route('categories.index');
}
case 'validate_only':
$messageBags = $repos->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('categories.edit', $category->id)->withInput();
break;
} }

View File

@ -8,6 +8,38 @@ use Firefly\Exception\FireflyException;
class GoogleTableController extends BaseController class GoogleTableController extends BaseController
{ {
/**
* @return \Illuminate\Http\JsonResponse
*/
public function categoryList()
{
/** @var \FireflyIII\Database\Category $repos */
$repos = App::make('FireflyIII\Database\Category');
/** @var \Grumpydictator\Gchart\GChart $chart */
$chart = App::make('gchart');
$chart->addColumn('ID', 'number');
$chart->addColumn('ID_Edit', 'string');
$chart->addColumn('ID_Delete', 'string');
$chart->addColumn('Name_URL', 'string');
$chart->addColumn('Name', 'string');
$list = $repos->get();
/** @var Category $entry */
foreach ($list as $entry) {
$chart->addRow(
$entry->id, route('categories.edit', $entry->id), route('categories.delete', $entry->id), route('categories.show', $entry->id), $entry->name
);
}
$chart->generate();
return Response::json($chart->getData());
}
/** /**
* @param $what * @param $what
* *
@ -84,8 +116,8 @@ class GoogleTableController extends BaseController
if (is_null($repetition)) { if (is_null($repetition)) {
$journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])->orderBy('date', 'DESC')->get(); $journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])->orderBy('date', 'DESC')->get();
} else { } else {
$journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])-> $journals = $budget->transactionjournals()->with(['budgets', 'categories', 'transactions', 'transactions.account'])->after($repetition->startdate)
after($repetition->startdate)->before($repetition->enddate)->orderBy('date', 'DESC')->get(); ->before($repetition->enddate)->orderBy('date', 'DESC')->get();
} }
/** @var TransactionJournal $transaction */ /** @var TransactionJournal $transaction */
foreach ($journals as $journal) { foreach ($journals as $journal) {

View File

@ -9,6 +9,7 @@ use Illuminate\Support\Collection;
use FireflyIII\Database\Ifaces\CommonDatabaseCalls; use FireflyIII\Database\Ifaces\CommonDatabaseCalls;
use FireflyIII\Database\Ifaces\CUD; use FireflyIII\Database\Ifaces\CUD;
use FireflyIII\Database\Ifaces\BudgetInterface; use FireflyIII\Database\Ifaces\BudgetInterface;
/** /**
* Class Budget * Class Budget
* *
@ -80,11 +81,11 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
$successes = new MessageBag; $successes = new MessageBag;
$errors = new MessageBag; $errors = new MessageBag;
if(isset($model['name'])) { if (isset($model['name'])) {
if(strlen($model['name']) < 1) { if (strlen($model['name']) < 1) {
$errors->add('name', 'Name is too short'); $errors->add('name', 'Name is too short');
} }
if(strlen($model['name']) > 200) { if (strlen($model['name']) > 200) {
$errors->add('name', 'Name is too long'); $errors->add('name', 'Name is too long');
} }
@ -98,8 +99,8 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
} }
if(!$errors->has('name')) { if (!$errors->has('name')) {
$successes->add('name','OK'); $successes->add('name', 'OK');
} }
return [ return [
@ -118,7 +119,7 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
{ {
$data['user_id'] = $this->getUser()->id; $data['user_id'] = $this->getUser()->id;
$budget = new \Budget($data); $budget = new \Budget($data);
$budget->class = 'Budget'; $budget->class = 'Budget';
if (!$budget->validate()) { if (!$budget->validate()) {
@ -155,10 +156,12 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
/** /**
* @param \Budget $budget * @param \Budget $budget
* @param Carbon $date * @param Carbon $date
*
* @return float * @return float
*/ */
public function spentInMonth(\Budget $budget, Carbon $date) { public function spentInMonth(\Budget $budget, Carbon $date)
{
$end = clone $date; $end = clone $date;
$date->startOfMonth(); $date->startOfMonth();
$end->endOfMonth(); $end->endOfMonth();
@ -220,7 +223,7 @@ class Budget implements CUD, CommonDatabaseCalls, BudgetInterface
*/ */
public function update(Ardent $model, array $data) public function update(Ardent $model, array $data)
{ {
$model->name = $data['name']; $model->name = $data['name'];
if (!$model->validate()) { if (!$model->validate()) {
var_dump($model->errors()->all()); var_dump($model->errors()->all());
exit; exit;

View File

@ -2,6 +2,7 @@
namespace FireflyIII\Database; namespace FireflyIII\Database;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent; use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -33,7 +34,8 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface
*/ */
public function destroy(Ardent $model) public function destroy(Ardent $model)
{ {
// TODO: Implement destroy() method. $model->delete();
return true;
} }
/** /**
@ -59,7 +61,37 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface
*/ */
public function validate(array $model) public function validate(array $model)
{ {
// TODO: Implement validate() method. $warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (isset($model['name'])) {
if (strlen($model['name']) < 1) {
$errors->add('name', 'Name is too short');
}
if (strlen($model['name']) > 200) {
$errors->add('name', 'Name is too long');
}
} else {
$errors->add('name', 'Name is mandatory');
}
$validator = \Validator::make($model, \Component::$rules);
if ($validator->invalid()) {
$errors->merge($validator->errors());
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
} }
/** /**
@ -91,7 +123,7 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface
*/ */
public function get() public function get()
{ {
// TODO: Implement get() method. return $this->getUser()->categories()->orderBy('name', 'ASC')->get();
} }
/** /**
@ -124,6 +156,15 @@ class Category implements CUD, CommonDatabaseCalls, CategoryInterface
*/ */
public function update(Ardent $model, array $data) public function update(Ardent $model, array $data)
{ {
// TODO: Implement update() method. $model->name = $data['name'];
if (!$model->validate()) {
var_dump($model->errors()->all());
exit;
}
$model->save();
return true;
} }
} }

View File

@ -174,6 +174,7 @@ Route::group(
// google table controller // google table controller
Route::get('/table/account/{account}/transactions', ['uses' => 'GoogleTableController@transactionsByAccount']); Route::get('/table/account/{account}/transactions', ['uses' => 'GoogleTableController@transactionsByAccount']);
Route::get('/table/accounts/{what}', ['uses' => 'GoogleTableController@accountList']); Route::get('/table/accounts/{what}', ['uses' => 'GoogleTableController@accountList']);
Route::get('/table/categories', ['uses' => 'GoogleTableController@categoryList']);
Route::get('/table/budget/{budget}/{limitrepetition?}/transactions', ['uses' => 'GoogleTableController@transactionsByBudget']); Route::get('/table/budget/{budget}/{limitrepetition?}/transactions', ['uses' => 'GoogleTableController@transactionsByBudget']);

View File

@ -1,13 +1,5 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Bla text here.
</p>
</div>
</div>
{{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}} {{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">

View File

@ -40,7 +40,4 @@
<script src="assets/javascript/firefly/accounts.js"></script> <script src="assets/javascript/firefly/accounts.js"></script>
@stop @stop
@section('styles')
@endsection

View File

@ -1,37 +1,31 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Remember that deleting something is permanent.
</p>
</div>
</div><!-- TODO cleanup to match new theme & form -->
{{Form::open(['class' => 'form-horizontal','url' => route('categories.destroy',$category->id)])}} {{Form::open(['class' => 'form-horizontal','url' => route('categories.destroy',$category->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
@if($category->transactionjournals()->count() > 0) <div class="panel panel-red">
<p class="text-info"> <div class="panel-heading">
Delete category "{{{$category->name}}}"
</div>
<div class="panel-body">
<p>
Are you sure?
</p>
Account "{{{$category->name}}}" still has {{$category->transactionjournals()->count()}} transaction(s) associated to it. <p>
These will NOT be deleted but will lose their connection to the category. <button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
</p> <a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >
@endif </p>
</div>
<p class="text-danger"> </div>
Press "Delete permanently" If you are sure you want to delete "{{{$category->name}}}".
</p>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> <div class="form-group">
<div class="col-sm-8"> <div class="col-sm-8">
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{route('categories.index')}}" class="btn-default btn">Cancel</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,46 +1,37 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
{{Form::model($category, ['class' => 'form-horizontal','url' => route('categories.update',$category->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<p class="lead">Use categories to group your expenses</p> <div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-exclamation"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
</div>
</div>
<p>
<button type="submit" class="btn btn-lg btn-success">
Update category
</button>
</p>
</div> </div>
</div><!-- TODO cleanup to match new theme & form --> <div class="col-lg-6 col-md-6 col-sm-12">
{{Form::open(['class' => 'form-horizontal','url' => route('categories.update',$category->id)])}}
<div class="row"> <!-- panel for options -->
<div class="col-lg-6 col-md-12 col-sm-6"> <div class="panel panel-default">
<h4>Mandatory fields</h4> <div class="panel-heading">
<i class="fa fa-bolt"></i> Options
<div class="form-group"> </div>
<label for="name" class="col-sm-4 control-label">Name</label> <div class="panel-body">
<div class="col-sm-8"> {{Form::ffOptionsList('update','category')}}
<input type="text" name="name" class="form-control" id="name" value="{{Input::old('name') ?: $category->name}}" placeholder="Name">
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span class="help-block">For example: bike, utilities, daily groceries</span>
@endif
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-6">
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Update the category</button>
</div>
</div>
</div>
</div> </div>
{{Form::close()}} {{Form::close()}}
@stop
@stop

View File

@ -2,37 +2,38 @@
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">Use categories to group your expenses</p> <div class="panel panel-default">
<p class="text-info"> <div class="panel-heading">
Use categories to group expenses by hobby, for certain types of groceries or what bills are for. <i class="fa {{{$mainTitleIcon}}}"></i> Categories
Expenses grouped in categories do not have to reoccur every month or every week, like budgets.
</p>
<p>
<a href="{{route('categories.create')}}" class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> Create a new category</a>
</p>
</div>
</div><!-- TODO cleanup to match new theme & form -->
<div class="row"> <!-- ACTIONS MENU -->
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="pull-right">
<table class="table table-striped"> <div class="btn-group">
<tr> <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
</tr> <span class="caret"></span>
@foreach($categories as $category) </button>
<tr> <ul class="dropdown-menu pull-right" role="menu">
<td> <li><a href="{{route('categories.create')}}"><i class="fa fa-plus fa-fw"></i> New category</a></li>
<a href="{{route('categories.show',$category->id)}}">{{{$category->name}}}</a> </ul>
</td>
<td>
<div class="btn-group btn-group-xs">
<a href="{{route('categories.edit',$category->id)}}" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="{{route('categories.delete',$category->id)}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</div> </div>
</td> </div>
</tr>
@endforeach
</table> </div>
<div class="panel-body">
<div id="category-list"></div>
</div>
</div>
</div> </div>
</div> </div>
@stop
@section('scripts')
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
{{HTML::script('assets/javascript/firefly/gcharts.options.js')}}
{{HTML::script('assets/javascript/firefly/gcharts.js')}}
<script src="assets/javascript/firefly/categories.js"></script>
@stop @stop

View File

@ -1,90 +1,94 @@
$(function () { $(function () {
if($('#chart').length == 1) {
/**
* get data from controller for home charts:
*/
$.getJSON('chart/categories/show/' + categoryID).success(function (data) {
var options = {
chart: {
renderTo: 'chart',
type: 'column'
},
series: [data.series],
title: {
text: data.chart_title
},
yAxis: {
formatter: function () {
return '$' + Highcharts.numberFormat(this.y, 0);
}
},
subtitle: {
text: data.subtitle,
useHTML: true
},
xAxis: { if (typeof googleTable == 'function') {
floor: 0, googleTable('table/categories', 'category-list');
type: 'category', }
title: {
text: 'Period' if ($('#chart').length == 1) {
} /**
}, * get data from controller for home charts:
tooltip: { */
shared: true, $.getJSON('chart/categories/show/' + categoryID).success(function (data) {
crosshairs: false, var options = {
formatter: function () { chart: {
var str = '<span style="font-size:80%;">' + Highcharts.dateFormat("%A, %e %B", this.x) + '</span><br />'; renderTo: 'chart',
for (x in this.points) { type: 'column'
var point = this.points[x];
var colour = point.point.pointAttr[''].fill;
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: \u20AC ' + Highcharts.numberFormat(point.y, 2) + '<br />';
}
//console.log();
return str;
return '<span style="font-size:80%;">' + this.series.name + ' on ' + Highcharts.dateFormat("%e %B", this.x) + ':</span><br /> \u20AC ' + Highcharts.numberFormat(this.y, 2);
}
},
plotOptions: {
line: {
shadow: true
}, },
series: { series: [data.series],
cursor: 'pointer', title: {
negativeColor: '#FF0000', text: data.chart_title
threshold: 0, },
lineWidth: 1, yAxis: {
marker: { formatter: function () {
radius: 2 return '$' + Highcharts.numberFormat(this.y, 0);
}
},
subtitle: {
text: data.subtitle,
useHTML: true
},
xAxis: {
floor: 0,
type: 'category',
title: {
text: 'Period'
}
},
tooltip: {
shared: true,
crosshairs: false,
formatter: function () {
var str = '<span style="font-size:80%;">' + Highcharts.dateFormat("%A, %e %B", this.x) + '</span><br />';
for (x in this.points) {
var point = this.points[x];
var colour = point.point.pointAttr[''].fill;
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: \u20AC ' + Highcharts.numberFormat(point.y, 2) + '<br />';
}
//console.log();
return str;
return '<span style="font-size:80%;">' + this.series.name + ' on ' + Highcharts.dateFormat("%e %B", this.x) + ':</span><br /> \u20AC ' + Highcharts.numberFormat(this.y, 2);
}
},
plotOptions: {
line: {
shadow: true
}, },
point: { series: {
events: { cursor: 'pointer',
click: function (e) { negativeColor: '#FF0000',
hs.htmlExpand(null, { threshold: 0,
src: 'chart/home/info/' + this.series.name + '/' + Highcharts.dateFormat("%d/%m/%Y", this.x), lineWidth: 1,
pageOrigin: { marker: {
x: e.pageX, radius: 2
y: e.pageY },
}, point: {
objectType: 'ajax', events: {
headingText: '<a href="#">' + this.series.name + '</a>', click: function (e) {
width: 250 hs.htmlExpand(null, {
} src: 'chart/home/info/' + this.series.name + '/' + Highcharts.dateFormat("%d/%m/%Y", this.x),
) pageOrigin: {
; x: e.pageX,
y: e.pageY
},
objectType: 'ajax',
headingText: '<a href="#">' + this.series.name + '</a>',
width: 250
}
)
;
}
} }
} }
} }
},
credits: {
enabled: false
} }
}, };
credits: { $('#chart').highcharts(options);
enabled: false });
} }
};
$('#chart').highcharts(options);
});
}
}); });