diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 8bc05e7bc8..2aeac51618 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -7,10 +7,10 @@ use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Pagination\LengthAwarePaginator; +use Input; use Redirect; use Session; use View; -use Input; /** @@ -38,44 +38,6 @@ class CategoryController extends Controller return view('categories.create')->with('subTitle', 'Create a new category'); } - /** - * @param Category $category - * - * @return $this - */ - public function show(Category $category, CategoryRepositoryInterface $repository) - { - $hideCategory = true; // used in list. - $page = intval(Input::get('page')); - $offset = $page > 0 ? $page * 50 : 0; - $set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']); - $count = $category->transactionJournals()->count(); - - $journals = new LengthAwarePaginator($set, $count, 50, $page); - - return view('categories.show', compact('category', 'journals', 'hideCategory')); - } - - /** - * @return \Illuminate\View\View - */ - public function noCategory() - { - $start = Session::get('start', Carbon::now()->startOfMonth()); - $end = Session::get('end', Carbon::now()->startOfMonth()); - $list = Auth::user() - ->transactionjournals() - ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('category_transaction_journal.id') - ->before($end) - ->after($start) - ->orderBy('transaction_journals.date') - ->get(['transaction_journals.*']); - $subTitle = 'Transactions without a category in ' . $start->format('F Y'); - - return view('categories.noCategory', compact('list', 'subTitle')); - } - /** * @param Category $category * @@ -122,11 +84,60 @@ class CategoryController extends Controller */ public function index() { - $categories = Auth::user()->categories()->get(); + $categories = Auth::user()->categories()->orderBy('name', 'ASC')->get(); + + $categories->each( + function (Category $category) { + $latest = $category->transactionjournals()->orderBy('date', 'DESC')->first(); + if ($latest) { + $category->lastActivity = $latest->date; + } + } + ); return view('categories.index', compact('categories')); } + /** + * @return \Illuminate\View\View + */ + public function noCategory() + { + $start = Session::get('start', Carbon::now()->startOfMonth()); + $end = Session::get('end', Carbon::now()->startOfMonth()); + $list = Auth::user() + ->transactionjournals() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->whereNull('category_transaction_journal.id') + ->before($end) + ->after($start) + ->orderBy('transaction_journals.date') + ->get(['transaction_journals.*']); + $subTitle = 'Transactions without a category in ' . $start->format('F Y'); + + return view('categories.noCategory', compact('list', 'subTitle')); + } + + /** + * @param Category $category + * + * @return $this + */ + public function show(Category $category, CategoryRepositoryInterface $repository) + { + $hideCategory = true; // used in list. + $page = intval(Input::get('page')); + $offset = $page > 0 ? $page * 50 : 0; + $set = $category->transactionJournals()->withRelevantData()->take(50)->offset($offset)->orderBy('date', 'DESC')->get( + ['transaction_journals.*'] + ); + $count = $category->transactionJournals()->count(); + + $journals = new LengthAwarePaginator($set, $count, 50, $page); + + return view('categories.show', compact('category', 'journals', 'hideCategory')); + } + /** * @param CategoryFormRequest $request * @param CategoryRepositoryInterface $repository diff --git a/app/Http/Controllers/GoogleChartController.php b/app/Http/Controllers/GoogleChartController.php index 110a0ddbeb..171fde76d7 100644 --- a/app/Http/Controllers/GoogleChartController.php +++ b/app/Http/Controllers/GoogleChartController.php @@ -13,6 +13,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\Preference; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Bill\BillRepositoryInterface; @@ -455,36 +456,32 @@ class GoogleChartController extends Controller /** * - * @param Category $category - * @param $year + * @param Category $category * * @return \Illuminate\Http\JsonResponse */ - public function categoriesAndSpending(Category $category, $year, GChart $chart) + public function categoryOverviewChart(Category $category, GChart $chart) { - try { - new Carbon('01-01-' . $year); - } catch (Exception $e) { - return view('error')->with('message', 'Invalid year.'); - } + // oldest transaction in category: + /** @var TransactionJournal $first */ + $first = $category->transactionjournals()->orderBy('date', 'ASC')->first(); + $start = $first->date; + /** @var Preference $range */ + $range = Preferences::get('viewRange', '1M'); + // jump to start of week / month / year / etc (TODO). + $start = Navigation::startOfPeriod($start, $range->data); - $chart->addColumn('Month', 'date'); - $chart->addColumn('Budgeted', 'number'); + $chart->addColumn('Period', 'date'); $chart->addColumn('Spent', 'number'); - $start = new Carbon('01-01-' . $year); - $end = clone $start; - $end->endOfYear(); + $end = new Carbon; while ($start <= $end) { - $currentEnd = clone $start; - $currentEnd->endOfMonth(); - $spent = floatval($category->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1; - $budgeted = null; + $currentEnd = Navigation::endOfPeriod($start, $range->data); + $spent = floatval($category->transactionjournals()->before($currentEnd)->after($start)->lessThan(0)->sum('amount')) * -1; + $chart->addRow(clone $start, $spent); - $chart->addRow(clone $start, $budgeted, $spent); - - $start->addMonth(); + $start = Navigation::addPeriod($start, $range->data, 0); } @@ -495,6 +492,36 @@ class GoogleChartController extends Controller } + /** + * + * @param Category $category + * + * @return \Illuminate\Http\JsonResponse + */ + public function categoryPeriodChart(Category $category, GChart $chart) + { + // oldest transaction in category: + /** @var TransactionJournal $first */ + $start = Session::get('start'); + $chart->addColumn('Period', 'date'); + $chart->addColumn('Spent', 'number'); + + $end = Session::get('end'); + while ($start <= $end) { + $spent = floatval($category->transactionjournals()->onDate($start)->lessThan(0)->sum('amount')) * -1; + $chart->addRow(clone $start, $spent); + $start->addDay(); + } + + $chart->generate(); + + return Response::json($chart->getData()); + + + } + + + /** * @param PiggyBank $piggyBank * diff --git a/app/Http/Controllers/RelatedController.php b/app/Http/Controllers/RelatedController.php index 9ea88a1352..59f112b2d7 100644 --- a/app/Http/Controllers/RelatedController.php +++ b/app/Http/Controllers/RelatedController.php @@ -10,6 +10,8 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; use Response; use Input; +use Redirect; +use URL; /** * Class RelatedController @@ -128,6 +130,34 @@ class RelatedController extends Controller return Response::json(true); } + /** + * @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind. + * + * @param TransactionJournal $parentJournal + * @param TransactionJournal $childJournal + * + * @return \Illuminate\Http\JsonResponse + * @throws Exception + */ + public function getRemoveRelation(TransactionJournal $parentJournal, TransactionJournal $childJournal) + { + $groups = $parentJournal->transactiongroups()->get(); + /** @var TransactionGroup $group */ + foreach ($groups as $group) { + foreach ($group->transactionjournals()->get() as $loopJournal) { + if ($loopJournal->id == $childJournal->id) { + // remove from group: + $group->transactionjournals()->detach($childJournal); + } + } + if ($group->transactionjournals()->count() == 1) { + $group->delete(); + } + } + + return Redirect::to(URL::previous()); + } + /** * @param TransactionJournal $journal * @@ -138,19 +168,9 @@ class RelatedController extends Controller $search = e(trim(Input::get('searchValue'))); - $result = $repository->searchRelated($search, $journal); - $result->each( - function (TransactionJournal $journal) { - /** @var Transaction $t */ - foreach ($journal->transactions()->get() as $t) { - if ($t->amount > 0) { - $journal->amount = $t->amount; - } - } - } - ); + $journals = $repository->searchRelated($search, $journal); + return view('related.searchResult',compact('journals')); - return Response::json($result->toArray()); } } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 1954c77230..49bd53d8b0 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -229,18 +229,9 @@ class TransactionController extends Controller $t->after = $t->before + $t->amount; } ); - $members = new Collection; - /** @var TransactionGroup $group */ - foreach ($journal->transactiongroups()->get() as $group) { - /** @var TransactionJournal $loopJournal */ - foreach ($group->transactionjournals()->get() as $loopJournal) { - if ($loopJournal->id != $journal->id) { - $members->push($loopJournal); - } - } - } - return view('transactions.show', compact('journal', 'members'))->with( + + return view('transactions.show', compact('journal'))->with( 'subTitle', e($journal->transactiontype->type) . ' "' . e($journal->description) . '"' ); } diff --git a/app/Http/routes.php b/app/Http/routes.php index c7e9478a67..664c1dd38b 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -230,12 +230,15 @@ Route::group( Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']); Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']); Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']); - Route::get('/chart/category/{category}/spending/{year}', ['uses' => 'GoogleChartController@categoriesAndSpending']); + Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']); Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']); Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']); Route::get('/chart/piggy-history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']); + Route::get('/chart/category/{category}/period', ['uses' => 'GoogleChartController@categoryPeriodChart']); + Route::get('/chart/category/{category}/overview', ['uses' => 'GoogleChartController@categoryOverviewChart']); + /** * Help Controller */ @@ -284,6 +287,7 @@ Route::group( Route::get('/related/alreadyRelated/{tj}', ['uses' => 'RelatedController@alreadyRelated', 'as' => 'related.alreadyRelated']); Route::post('/related/relate/{tj}/{tjSecond}', ['uses' => 'RelatedController@relate', 'as' => 'related.relate']); Route::post('/related/removeRelation/{tj}/{tjSecond}', ['uses' => 'RelatedController@removeRelation', 'as' => 'related.removeRelation']); + Route::get('/related/remove/{tj}/{tjSecond}', ['uses' => 'RelatedController@getRemoveRelation', 'as' => 'related.getRemoveRelation']); Route::get('/related/related/{tj}', ['uses' => 'RelatedController@related', 'as' => 'related.related']); Route::post('/related/search/{tj}', ['uses' => 'RelatedController@search', 'as' => 'related.search']); diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index d4290ea33c..c2cfdfb436 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -28,20 +28,27 @@ class Navigation $add = ($skip + 1); $functionMap = [ + '1D' => 'addDays', 'daily' => 'addDays', + '1W' => 'addWeeks', 'weekly' => 'addWeeks', 'week' => 'addWeeks', + '1M' => 'addMonths', 'month' => 'addMonths', 'monthly' => 'addMonths', + '3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', + '6M' => 'addMonths', 'half-year' => 'addMonths', 'year' => 'addYears', 'yearly' => 'addYears', ]; $modifierMap = [ 'quarter' => 3, + '3M' => 3, 'quarterly' => 3, + '6M' => 6, 'half-year' => 6, ]; if (!isset($functionMap[$repeatFreq])) { @@ -68,24 +75,31 @@ class Navigation $currentEnd = clone $theCurrentEnd; $functionMap = [ + '1D' => 'addDay', 'daily' => 'addDay', + '1W' => 'addWeek', 'week' => 'addWeek', 'weekly' => 'addWeek', + '1M' => 'addMonth', 'month' => 'addMonth', 'monthly' => 'addMonth', + '3M' => 'addMonths', 'quarter' => 'addMonths', 'quarterly' => 'addMonths', + '6M' => 'addMonths', 'half-year' => 'addMonths', 'year' => 'addYear', 'yearly' => 'addYear', ]; $modifierMap = [ 'quarter' => 3, + '3M' => 3, 'quarterly' => 3, 'half-year' => 6, + '6M' => 6, ]; - $subDay = ['week', 'weekly', 'month', 'monthly', 'quarter', 'quarterly', 'half-year', 'year', 'yearly']; + $subDay = ['week', 'weekly', '1W', 'month', 'monthly', '1M', '3M', 'quarter', 'quarterly', '6M', 'half-year', 'year', 'yearly']; if (!isset($functionMap[$repeatFreq])) { throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq); @@ -298,11 +312,15 @@ class Navigation $date = clone $theDate; $functionMap = [ + '1D' => 'startOfDay', 'daily' => 'startOfDay', + '1W' => 'startOfWeek', 'week' => 'startOfWeek', 'weekly' => 'startOfWeek', 'month' => 'startOfMonth', + '1M' => 'startOfMonth', 'monthly' => 'startOfMonth', + '3M' => 'firstOfQuarter', 'quarter' => 'firstOfQuarter', 'quarterly' => 'firstOfQuarter', 'year' => 'startOfYear', @@ -314,7 +332,7 @@ class Navigation return $date; } - if ($repeatFreq == 'half-year') { + if ($repeatFreq == 'half-year' || $repeatFreq == '6M') { $month = intval($date->format('m')); $date->startOfYear(); if ($month >= 7) { diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 1d06ba7c38..f906c5cb6d 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -41,7 +41,7 @@ class FireflyValidator extends Validator */ public function validateUniqueForUser($attribute, $value, $parameters) { - $count = DB::table($parameters[0])->where($parameters[1], $value)->count(); + $count = DB::table($parameters[0])->where($parameters[1], $value)->where('id', '!=', $parameters[2])->count(); if ($count == 0) { return true; } diff --git a/public/css/firefly.css b/public/css/firefly.css index 07544b7a03..15f7ef3e7a 100644 --- a/public/css/firefly.css +++ b/public/css/firefly.css @@ -1 +1,2 @@ -#daterange {cursor:pointer;} \ No newline at end of file +#daterange {cursor:pointer;} +.google-chart-error {height:30px;background:url('/images/error.png') no-repeat center center;} \ No newline at end of file diff --git a/public/js/categories.js b/public/js/categories.js index 694754351b..01c72143d9 100644 --- a/public/js/categories.js +++ b/public/js/categories.js @@ -1,7 +1,8 @@ $(function () { - if (typeof componentID !== 'undefined' && typeof repetitionID === 'undefined') { - googleColumnChart('chart/category/' + componentID + '/spending/' + year, 'componentOverview'); + if (typeof categoryID !== 'undefined') { + googleColumnChart('chart/category/' + categoryID + '/overview', 'componentOverview'); + googleColumnChart('chart/category/' + categoryID + '/period', 'periodOverview'); } diff --git a/public/js/firefly.js b/public/js/firefly.js index cf002bd366..0bf2c43a4f 100644 --- a/public/js/firefly.js +++ b/public/js/firefly.js @@ -2,39 +2,38 @@ $(function () { $('.currencySelect').click(currencySelect); - $('#daterange').daterangepicker( - { - ranges: { - 'This Month': [moment().startOf('month'), moment().endOf('month')], - 'Last Month': [moment().subtract('month', 1).startOf('month'), moment().subtract('month', 1).endOf('month')], - 'Next Month': [moment().add('month', 1).startOf('month'), moment().add('month', 1).endOf('month')], - 'Everything': [firstDate, moment()] - }, - opens: 'left', - - format: 'DD-MM-YYYY', - startDate: start, - endDate: end + $('#daterange').daterangepicker( + { + ranges: { + 'This Month': [moment().startOf('month'), moment().endOf('month')], + 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')], + 'Next Month': [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')], + 'Everything': [firstDate, moment()] }, - function(start, end, label) { + opens: 'left', - // send post. - $.post(dateRangeURL, { - start: start.format('YYYY-MM-DD'), - end: end.format('YYYY-MM-DD'), - label: label, - _token: token - }).success(function() { - window.location.reload(true); - }).fail(function() { - alert('Could not change date range'); + format: 'DD-MM-YYYY', + startDate: start, + endDate: end + }, + function (start, end, label) { - }); + // send post. + $.post(dateRangeURL, { + start: start.format('YYYY-MM-DD'), + end: end.format('YYYY-MM-DD'), + label: label, + _token: token + }).success(function () { + window.location.reload(true); + }).fail(function () { + alert('Could not change date range'); - //alert('A date range was chosen: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD')); - } + }); - ); + //alert('A date range was chosen: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD')); + } + ); }); diff --git a/public/js/related-manager.js b/public/js/related-manager.js index 55cdd19431..77f9860402 100644 --- a/public/js/related-manager.js +++ b/public/js/related-manager.js @@ -42,20 +42,9 @@ function searchRelatedTransactions(e, ID) { var searchValue = $('#relatedSearchValue').val(); if (searchValue != '') { $.post('related/search/' + ID, {searchValue: searchValue,_token:token}).success(function (data) { - // post each result to some div. - $('#relatedSearchResults').empty(); - - $.each(data, function (i, row) { - var tr = $(''); - - var checkBox = $('').append($('').attr('type', 'checkbox').data('relateto', ID).data('id', row.id).click(doRelateNewTransaction)); - var description = $('').text(row.description); - var amount = $('').html(row.amount); - tr.append(checkBox).append(description).append(amount); - $('#relatedSearchResults').append(tr); - //$('#relatedSearchResults').append($('
').text(row.id)); - }); - + // post the results to some div. + $('#relatedSearchResultsTitle').show(); + $('#relatedSearchResults').empty().html(data); }).fail(function () { alert('Could not search. Sorry.'); diff --git a/resources/views/categories/show.blade.php b/resources/views/categories/show.blade.php index fd17e123e8..b99ffb9cc5 100644 --- a/resources/views/categories/show.blade.php +++ b/resources/views/categories/show.blade.php @@ -2,7 +2,17 @@ @section('content') {!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $category) !!}
-
+
+
+
+ Overview +
+
+
+
+
+
+
Overview @@ -11,6 +21,10 @@
+
+
+
+
@@ -21,16 +35,12 @@
-
- (TODO) -
@stop @section('scripts') diff --git a/resources/views/layouts/default.blade.php b/resources/views/layouts/default.blade.php index b02c5b9bf2..cf12b29c91 100644 --- a/resources/views/layouts/default.blade.php +++ b/resources/views/layouts/default.blade.php @@ -109,7 +109,6 @@ diff --git a/resources/views/list/categories.blade.php b/resources/views/list/categories.blade.php index 789481e63e..e70c2788b7 100644 --- a/resources/views/list/categories.blade.php +++ b/resources/views/list/categories.blade.php @@ -21,8 +21,11 @@ {{{$category->name}}} - + @if($category->lastActivity) + {{$category->lastActivity->format('jS F Y')}} + @else Never + @endif @endforeach diff --git a/resources/views/preferences/index.blade.php b/resources/views/preferences/index.blade.php index 6f4c55e69d..828eb69402 100644 --- a/resources/views/preferences/index.blade.php +++ b/resources/views/preferences/index.blade.php @@ -45,10 +45,10 @@
- Home view range + View range
-

By default, Firefly will show you one month of data.

+

Some charts are automatically grouped in periods. What period would you prefer?