From 92f2e30ed1831f296b0facd439f66cb63998834c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 27 Jul 2014 20:29:58 +0200 Subject: [PATCH] New stuff! [skip ci] --- app/assets/javascripts/accounts.js | 16 + app/assets/javascripts/firefly/accounts.js | 95 ++++++ app/assets/stylesheets/accounts.css | 13 + app/controllers/AccountController.php | 12 +- app/controllers/BudgetController.php | 118 ++++---- .../Firefly/Helper/Controllers/Account.php | 116 ++++++++ .../Helper/Controllers/AccountInterface.php | 8 + app/lib/Firefly/Helper/Controllers/Budget.php | 61 ++++ .../Helper/Controllers/BudgetInterface.php | 18 ++ .../Firefly/Helper/HelperServiceProvider.php | 5 + .../Budget/EloquentBudgetRepository.php | 62 ++-- .../Storage/Limit/EloquentLimitRepository.php | 2 +- app/models/LimitRepetition.php | 17 +- app/routes.php | 23 +- .../controllers/AccountControllerTest.php | 274 ++++++++++++------ app/views/accounts/index.blade.php | 5 - app/views/accounts/show.blade.php | 90 +++++- app/views/budgets/indexByBudget.blade.php | 47 +-- app/views/budgets/indexByDate.blade.php | 62 ++-- app/views/index.blade.php | 2 +- app/views/paginated/transactions.blade.php | 71 +++++ phpunit.xml | 2 +- 22 files changed, 868 insertions(+), 251 deletions(-) create mode 100644 app/assets/javascripts/accounts.js create mode 100644 app/assets/javascripts/firefly/accounts.js create mode 100644 app/assets/stylesheets/accounts.css create mode 100644 app/lib/Firefly/Helper/Controllers/Budget.php create mode 100644 app/lib/Firefly/Helper/Controllers/BudgetInterface.php create mode 100644 app/views/paginated/transactions.blade.php diff --git a/app/assets/javascripts/accounts.js b/app/assets/javascripts/accounts.js new file mode 100644 index 0000000000..a9d3956059 --- /dev/null +++ b/app/assets/javascripts/accounts.js @@ -0,0 +1,16 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear in whatever order it +// gets included (e.g. say you have require_tree . then the code will appear after all the directories +// but before any files alphabetically greater than 'application.js' +// +// The available directives right now are require, require_directory, and require_tree +// +//= require highslide/highslide-full.min +//= require highslide/highslide.config +//= require_tree highcharts +//= require firefly/accounts diff --git a/app/assets/javascripts/firefly/accounts.js b/app/assets/javascripts/firefly/accounts.js new file mode 100644 index 0000000000..67040dd4d5 --- /dev/null +++ b/app/assets/javascripts/firefly/accounts.js @@ -0,0 +1,95 @@ +$(function () { +if($('#chart').length == 1) { + /** + * get data from controller for home charts: + */ + $.getJSON('chart/home/account/' + accountID).success(function (data) { + var options = { + chart: { + renderTo: 'chart', + type: 'line' + }, + + series: data, + title: { + text: 'BETTER TITLE HERE' + }, + yAxis: { + formatter: function () { + return '$' + Highcharts.numberFormat(this.y, 0); + } + }, + subtitle: { + text: 'View more', + useHTML: true + }, + + xAxis: { + floor: 0, + type: 'datetime', + dateTimeLabelFormats: { + day: '%e %b', + year: '%b' + }, + title: { + text: 'Date' + } + }, + tooltip: { + shared: true, + crosshairs: false, + formatter: function () { + var str = '' + Highcharts.dateFormat("%A, %e %B", this.x) + '
'; + for (x in this.points) { + var point = this.points[x]; + var colour = point.point.pointAttr[''].fill; + str += '' + point.series.name + ': € ' + Highcharts.numberFormat(point.y, 2) + '
'; + } + //console.log(); + return str; + return '' + this.series.name + ' on ' + Highcharts.dateFormat("%e %B", this.x) + ':
€ ' + Highcharts.numberFormat(this.y, 2); + } + }, + plotOptions: { + line: { + shadow: true + }, + series: { + cursor: 'pointer', + negativeColor: '#FF0000', + threshold: 0, + lineWidth: 1, + marker: { + radius: 2 + }, + point: { + events: { + click: function (e) { + 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: '' + this.series.name + '', + width: 250 + } + ) + ; + } + } + } + } + }, + credits: { + enabled: false + } + }; + $('#chart').highcharts(options); + }); +} + + + +}); \ No newline at end of file diff --git a/app/assets/stylesheets/accounts.css b/app/assets/stylesheets/accounts.css new file mode 100644 index 0000000000..87f24089da --- /dev/null +++ b/app/assets/stylesheets/accounts.css @@ -0,0 +1,13 @@ +/** + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any Css/Less files within this directory, lib/assets/javascripts, vendor/assets/javascripts, + * can be referenced here using a relative path. + * + * It's not advisable to add code directly here, but if you do, it'll appear in whatever order it + * gets included (e.g. say you have require_tree . then the code will appear after all the directories + * but before any files alphabetically greater than 'application.css' + * + *= require highslide/highslide + */ \ No newline at end of file diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 89703b3015..e4c3714734 100644 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -51,13 +51,11 @@ class AccountController extends \BaseController $result = $this->_repository->destroy(Input::get('id')); if ($result === true) { Session::flash('success', 'The account was deleted.'); - - return Redirect::route('accounts.index'); } else { - Session::flash('danger', 'Could not delete the account. Check the logs to be sure.'); - - return Redirect::route('accounts.index'); + Session::flash('error', 'Could not delete the account. Check the logs to be sure.'); } + return Redirect::route('accounts.index'); + } @@ -93,7 +91,9 @@ class AccountController extends \BaseController */ public function show(Account $account) { - return View::make('accounts.show')->with('account', $account); + $show = $this->_accounts->show($account, 40); + + return View::make('accounts.show')->with('account', $account)->with('show',$show); } /** diff --git a/app/controllers/BudgetController.php b/app/controllers/BudgetController.php index c95daffa2f..2c81e339a0 100644 --- a/app/controllers/BudgetController.php +++ b/app/controllers/BudgetController.php @@ -1,7 +1,8 @@ _budgets = $budgets; + $this->_repository = $repository; View::share('menu', 'budgets'); } + /** + * @return $this|\Illuminate\View\View + */ + public function create() + { + $periods = \Config::get('firefly.periods_to_text'); + + return View::make('budgets.create')->with('periods', $periods); + } + + /** + * @return $this|\Illuminate\View\View + */ + public function indexByBudget() + { + $budgets = $this->_repository->get(); + $today = new Carbon; + + return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', $today); + + } + /** * @return $this|\Illuminate\View\View * @throws Firefly\Exception\FireflyException @@ -26,76 +51,12 @@ class BudgetController extends BaseController public function indexByDate() { // get a list of dates by getting all repetitions: - $budgets = $this->_budgets->get(); - $reps = []; - foreach ($budgets as $budget) { - foreach ($budget->limits as $limit) { - $dateFormats = \Config::get('firefly.date_formats_by_period.' . $limit->repeat_freq); - if (is_null($dateFormats)) { - throw new \Firefly\Exception\FireflyException('No date formats for ' . $limit->repeat_freq); - } + $set = $this->_repository->get(); + $budgets = $this->_budgets->organizeByDate($set); - foreach ($limit->limitrepetitions as $rep) { - $periodOrder = $rep->startdate->format($dateFormats['group_date']); - $period = $rep->startdate->format($dateFormats['display_date']); - $reps[$periodOrder] = isset($reps[$periodOrder]) ? $reps[$periodOrder] : ['date' => $period]; + return View::make('budgets.indexByDate')->with('budgets', $budgets); - } - } - } - // put all the budgets under their respective date: - foreach ($budgets as $budget) { - foreach ($budget->limits as $limit) { - $dateFormats = \Config::get('firefly.date_formats_by_period.' . $limit->repeat_freq); - foreach ($limit->limitrepetitions as $rep) { - $month = $rep->startdate->format($dateFormats['group_date']); - $reps[$month]['limitrepetitions'][] = $rep; - } - } - } - krsort($reps); - - return View::make('budgets.indexByDate')->with('reps', $reps); - - } - - /** - * @return $this|\Illuminate\View\View - */ - public function indexByBudget() - { - $budgets = $this->_budgets->get(); - $today = new \Carbon\Carbon; - return View::make('budgets.indexByBudget')->with('budgets', $budgets)->with('today', $today); - - } - - /** - * @return $this|\Illuminate\View\View - */ - public function create() - { - $periods = \Config::get('firefly.periods_to_text'); - return View::make('budgets.create')->with('periods', $periods); - } - - /** - * @return \Illuminate\Http\RedirectResponse - */ - public function store() - { - - $data = [ - 'name' => Input::get('name'), - 'amount' => floatval(Input::get('amount')), - 'repeat_freq' => Input::get('period'), - 'repeats' => intval(Input::get('repeats')) - ]; - - $this->_budgets->store($data); - Session::flash('success', 'Budget created!'); - return Redirect::route('budgets.index'); } /** @@ -146,5 +107,24 @@ class BudgetController extends BaseController } + /** + * @return \Illuminate\Http\RedirectResponse + */ + public function store() + { + + $data = [ + 'name' => Input::get('name'), + 'amount' => floatval(Input::get('amount')), + 'repeat_freq' => Input::get('period'), + 'repeats' => intval(Input::get('repeats')) + ]; + + $this->_budgets->store($data); + Session::flash('success', 'Budget created!'); + + return Redirect::route('budgets.index'); + } + } \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Controllers/Account.php b/app/lib/Firefly/Helper/Controllers/Account.php index cc1bb527ed..bdc18aff7a 100644 --- a/app/lib/Firefly/Helper/Controllers/Account.php +++ b/app/lib/Firefly/Helper/Controllers/Account.php @@ -41,6 +41,7 @@ class Account implements AccountInterface } } + return $list; } @@ -53,6 +54,7 @@ class Account implements AccountInterface public function openingBalanceTransaction(\Account $account) { $transactionType = \TransactionType::where('type', 'Opening balance')->first(); + return \TransactionJournal:: with( ['transactions' => function ($q) { @@ -63,4 +65,118 @@ class Account implements AccountInterface ->where('transactions.account_id', $account->id)->first(['transaction_journals.*']); } + + /** + * @param \Account $account + * @param $perPage + * + * @return mixed|void + */ + public function show(\Account $account, $perPage) + { + $start = \Session::get('start'); + $end = \Session::get('end'); + $stats = [ + 'budgets' => [], + 'categories' => [], + 'accounts' => [] + ]; + $items = []; + + + // build a query: + $query = \TransactionJournal::with( + ['transactions' => function ($q) { + $q->orderBy('amount', 'ASC'); + }, 'transactiontype', 'components' => function ($q) { + $q->orderBy('class'); + }, 'transactions.account.accounttype'] + )->orderBy('date', 'DESC')->leftJoin( + 'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' + )->where('transactions.account_id', $account->id)->where('date', '>=', $start->format('Y-m-d'))->where( + 'date', '<=', $end->format('Y-m-d') + )->orderBy('transaction_journals.id', 'DESC'); + + + // build paginator: + $totalItems = $query->count(); + $page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; + $skip = ($page - 1) * $perPage; + $result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']); + // in the mean time, build list of categories, budgets and other accounts: + + /** @var $item \TransactionJournal */ + foreach ($result as $item) { + $items[] = $item; + foreach ($item->components as $component) { + if ($component->class == 'Budget') { + $stats['budgets'][$component->id] = $component; + } + if ($component->class == 'Category') { + $stats['categories'][$component->id] = $component; + } + } + $fromAccount = $item->transactions[0]->account; + $toAccount = $item->transactions[1]->account; + $stats['accounts'][$fromAccount->id] = $fromAccount; + $stats['accounts'][$toAccount->id] = $toAccount; + } + unset($result, $page); + $paginator = \Paginator::make($items, $totalItems, $perPage); + + // statistics + $stats['period']['in'] = floatval( + \Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->leftJoin( + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' + )->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where( + 'transaction_journals.date', '>=', $start->format('Y-m-d') + )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') + ); + + + $stats['period']['out'] = floatval( + \Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->leftJoin( + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' + )->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where( + 'transaction_journals.date', '>=', $start->format('Y-m-d') + )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') + ); + $stats['period']['diff'] = $stats['period']['in'] + $stats['period']['out']; + + $stats['period']['t_in'] = floatval( + \Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->leftJoin( + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' + )->where('transaction_types.type', 'Transfer')->where( + 'transaction_journals.date', '>=', $start->format('Y-m-d') + )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') + ); + + $stats['period']['t_out'] = floatval( + \Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin( + 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' + )->leftJoin( + 'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' + )->where('transaction_types.type', 'Transfer')->where( + 'transaction_journals.date', '>=', $start->format('Y-m-d') + )->where('transaction_journals.date', '<=', $end->format('Y-m-d'))->sum('amount') + ); + + $stats['period']['t_diff'] = $stats['period']['t_in'] + $stats['period']['t_out']; + + + $return = [ + 'journals' => $paginator, + 'statistics' => $stats + ]; + + return $return; + + + } } \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Controllers/AccountInterface.php b/app/lib/Firefly/Helper/Controllers/AccountInterface.php index 02efe5baf7..793035c043 100644 --- a/app/lib/Firefly/Helper/Controllers/AccountInterface.php +++ b/app/lib/Firefly/Helper/Controllers/AccountInterface.php @@ -28,4 +28,12 @@ interface AccountInterface */ public function openingBalanceTransaction(\Account $account); + /** + * @param \Account $account + * @param $perPage + * + * @return mixed + */ + public function show(\Account $account, $perPage); + } \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Controllers/Budget.php b/app/lib/Firefly/Helper/Controllers/Budget.php new file mode 100644 index 0000000000..9aad56d9cb --- /dev/null +++ b/app/lib/Firefly/Helper/Controllers/Budget.php @@ -0,0 +1,61 @@ +limits as $limit) { + $dateFormats = \Config::get('firefly.date_formats_by_period.' . $limit->repeat_freq); + if (is_null($dateFormats)) { + throw new \Firefly\Exception\FireflyException('No date formats for ' . $limit->repeat_freq); + } + + foreach ($limit->limitrepetitions as $rep) { + $periodOrder = $rep->startdate->format($dateFormats['group_date']); + $period = $rep->startdate->format($dateFormats['display_date']); + $return[$periodOrder] = isset($return[$periodOrder]) ? $return[$periodOrder] : ['date' => $period]; + + } + } + } + // put all the budgets under their respective date: + foreach ($budgets as $budget) { + foreach ($budget->limits as $limit) { + $dateFormats = \Config::get('firefly.date_formats_by_period.' . $limit->repeat_freq); + foreach ($limit->limitrepetitions as $rep) { + $rep->left = $rep->left(); + + $month = $rep->startdate->format($dateFormats['group_date']); + $return[$month]['limitrepetitions'][] = $rep; + } + } + } + krsort($return); + return $return; + } + +} \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Controllers/BudgetInterface.php b/app/lib/Firefly/Helper/Controllers/BudgetInterface.php new file mode 100644 index 0000000000..a727cf4f9b --- /dev/null +++ b/app/lib/Firefly/Helper/Controllers/BudgetInterface.php @@ -0,0 +1,18 @@ +app->bind( + 'Firefly\Helper\Controllers\BudgetInterface', + 'Firefly\Helper\Controllers\Budget' + ); + // mail: $this->app->bind( 'Firefly\Helper\Email\EmailHelperInterface', diff --git a/app/lib/Firefly/Storage/Budget/EloquentBudgetRepository.php b/app/lib/Firefly/Storage/Budget/EloquentBudgetRepository.php index c3945e10cb..113e2adc5b 100644 --- a/app/lib/Firefly/Storage/Budget/EloquentBudgetRepository.php +++ b/app/lib/Firefly/Storage/Budget/EloquentBudgetRepository.php @@ -12,6 +12,40 @@ use Carbon\Carbon; class EloquentBudgetRepository implements BudgetRepositoryInterface { + /** + * @param $budgetId + * + * @return mixed + */ + public function find($budgetId) + { + + return \Auth::user()->budgets()->find($budgetId); + } + + /** + * @return mixed + */ + public function get() + { + $set = \Auth::user()->budgets()->with( + ['limits' => function ($q) { + $q->orderBy('limits.startdate', 'ASC'); + }, 'limits.limitrepetitions' => function ($q) { + $q->orderBy('limit_repetitions.startdate', 'ASC'); + }] + )->orderBy('name', 'ASC')->get(); + foreach ($set as $budget) { + foreach ($budget->limits as $limit) { + foreach ($limit->limitrepetitions as $rep) { + $rep->left = $rep->left(); + } + } + } + + return $set; + } + /** * @return array|mixed */ @@ -24,6 +58,7 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface foreach ($list as $entry) { $return[intval($entry->id)] = $entry->name; } + return $return; } @@ -69,6 +104,7 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface $budget->count += count($limit->limitrepetitions); } } + return $set; } @@ -123,30 +159,4 @@ class EloquentBudgetRepository implements BudgetRepositoryInterface return $budget; } - - /** - * @return mixed - */ - public function get() - { - return \Auth::user()->budgets()->with( - ['limits' => function ($q) { - $q->orderBy('limits.startdate', 'ASC'); - }, 'limits.limitrepetitions' => function ($q) { - $q->orderBy('limit_repetitions.startdate', 'ASC'); - }] - )->orderBy('name', 'ASC')->get(); - } - - /** - * @param $budgetId - * - * @return mixed - */ - public function find($budgetId) - { - - return \Auth::user()->budgets()->find($budgetId); - } - } \ No newline at end of file diff --git a/app/lib/Firefly/Storage/Limit/EloquentLimitRepository.php b/app/lib/Firefly/Storage/Limit/EloquentLimitRepository.php index b241aef92d..96bcc3d607 100644 --- a/app/lib/Firefly/Storage/Limit/EloquentLimitRepository.php +++ b/app/lib/Firefly/Storage/Limit/EloquentLimitRepository.php @@ -100,7 +100,7 @@ class EloquentLimitRepository implements LimitRepositoryInterface */ public function getTJByBudgetAndDateRange(\Budget $budget, Carbon $start, Carbon $end) { - $result = $budget->transactionjournals()->after($start)->before($end)->get(); + $result = $budget->transactionjournals()->with('transactions')->after($start)->before($end)->get(); return $result; diff --git a/app/models/LimitRepetition.php b/app/models/LimitRepetition.php index df8c26e6f5..010d01d5f8 100644 --- a/app/models/LimitRepetition.php +++ b/app/models/LimitRepetition.php @@ -37,6 +37,7 @@ class LimitRepetition extends Ardent $start->startOfMonth(); $end = clone $start; $end->endOfMonth(); + return [ 'limit_id' => 'factory|Limit', 'startdate' => $start, @@ -45,11 +46,6 @@ class LimitRepetition extends Ardent ]; } - public function limit() - { - return $this->belongsTo('Limit'); - } - public function getDates() { return ['created_at', 'updated_at', 'startdate', 'enddate']; @@ -60,10 +56,6 @@ class LimitRepetition extends Ardent */ public function left() { - $key = 'limit-rep-left-' . $this->id; - if (Cache::has($key)) { - return Cache::get($key); - } $left = floatval($this->amount); // budget: @@ -80,11 +72,14 @@ class LimitRepetition extends Ardent } } } - Cache::forever($key, $left); - return $left; } + public function limit() + { + return $this->belongsTo('Limit'); + } + } \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index 6d379add58..15c5f10d2d 100644 --- a/app/routes.php +++ b/app/routes.php @@ -3,8 +3,23 @@ // models: Route::bind('account', function($value, $route) { - return Account::where('id', $value)->where('user_id',Auth::user()->id)->first(); + if(Auth::check()) { + return Account:: + where('id', $value)-> + where('user_id',Auth::user()->id)->first(); + } + return null; }); +Route::bind('budget', function($value, $route) + { + if(Auth::check()) { + return Budget:: + where('id', $value)-> + where('user_id',Auth::user()->id)->first(); + } + return null; + }); + // protected routes: @@ -15,7 +30,7 @@ Route::group(['before' => 'auth'], function () { Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']); // chart controller - Route::get('/chart/home/account/{account?}', ['uses' => 'ChartController@homeAccount', 'as' => 'chart.home']); + Route::get('/chart/home/account/{accountname?}', ['uses' => 'ChartController@homeAccount', 'as' => 'chart.home']); Route::get('/chart/home/categories', ['uses' => 'ChartController@homeCategories', 'as' => 'chart.categories']); Route::get('/chart/home/budgets', ['uses' => 'ChartController@homeBudgets', 'as' => 'chart.budgets']); Route::get('/chart/home/info/{accountname}/{day}/{month}/{year}', ['uses' => 'ChartController@homeAccountInfo', 'as' => 'chart.info']); @@ -42,7 +57,9 @@ Route::group(['before' => 'auth'], function () { Route::get('/budget/create',['uses' => 'BudgetController@create', 'as' => 'budgets.create']); Route::get('/budgets',['uses' => 'BudgetController@indexByDate','as' => 'budgets.index']); Route::get('/budgets/budget',['uses' => 'BudgetController@indexByBudget','as' => 'budgets.index.budget']); - Route::get('/budget/show/{id}',['uses' => 'BudgetController@show', 'as' => 'budgets.show']); + Route::get('/budget/show/{budget}',['uses' => 'BudgetController@show', 'as' => 'budgets.show']); + Route::get('/budget/edit/{budget}',['uses' => 'BudgetController@edit', 'as' => 'budgets.edit']); + Route::get('/budget/delete/{budget}',['uses' => 'BudgetController@delete', 'as' => 'budgets.delete']); // limit controller: Route::get('/budgets/limits/create/{id?}',['uses' => 'LimitController@create','as' => 'budgets.limits.create']); diff --git a/app/tests/controllers/AccountControllerTest.php b/app/tests/controllers/AccountControllerTest.php index 7f1bfd6a3b..8167685b4a 100644 --- a/app/tests/controllers/AccountControllerTest.php +++ b/app/tests/controllers/AccountControllerTest.php @@ -1,102 +1,210 @@ mock('AccountType'); - $personal->shouldReceive('getAttribute', 'description')->andReturn('Default account'); - - $bene = $this->mock('AccountType'); - $bene->shouldReceive('getAttribute', 'description')->andReturn('Beneficiary account'); - - $initial = $this->mock('AccountType'); - $initial->shouldReceive('getAttribute', 'description')->andReturn('Initial balance account'); - - $cash = $this->mock('AccountType'); - $cash->shouldReceive('getAttribute', 'description')->andReturn('Cash account'); - - - // mock account(s) - $one = $this->mock('Account'); - $one->shouldReceive('getAttribute')->andReturn($personal); - - $two = $this->mock('Account'); - $two->shouldReceive('getAttribute')->andReturn($bene); - - $three = $this->mock('Account'); - $three->shouldReceive('getAttribute')->andReturn($initial); - - $four = $this->mock('Account'); - $four->shouldReceive('getAttribute')->andReturn($cash); - $c = new \Illuminate\Database\Eloquent\Collection([$one, $two, $three, $four]); - - // mock account repository: - $accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface'); - $accounts->shouldReceive('get')->andReturn($c); - - - $list = [ - 'personal' => [$one], - 'beneficiaries' => [$two], - 'initial' => [$three], - 'cash' => [$four] - ]; - - // mock: - View::shouldReceive('share'); - View::shouldReceive('make')->with('accounts.index')->once()->andReturn(\Mockery::self()) - ->shouldReceive('with')->once()->with('accounts', $list)->andReturn(\Mockery::self()) - ->shouldReceive('with')->once()->with('total', 4)->andReturn(\Mockery::self()); - - - // call - $this->call('GET', '/accounts'); - - // test - $this->assertResponseOk(); + $this->_repository = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface'); + $this->_accounts = $this->mock('Firefly\Helper\Controllers\AccountInterface'); + $this->_user = m::mock('User','Eloquent'); + $this->app->instance('User', $this->_user); } - public function testCreate() - { - // mock: - View::shouldReceive('share'); - View::shouldReceive('make')->with('accounts.create'); - - // call - $this->call('GET', '/accounts/create'); - - // test - $this->assertResponseOk(); - } - - public function testShow() - { - // mock account repository: - $accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface'); - $accounts->shouldReceive('get')->with(1)->andReturn([]); - - // call - $this->call('GET', '/accounts/1'); - - // test - $this->assertResponseOk(); - } - - public function tearDown() { Mockery::close(); } + + public function testCreate() + { + $this->action('GET', 'AccountController@create'); + $this->assertResponseOk(); + + } + + public function testDelete() + { + + $account = f::create('Account'); + Auth::shouldReceive('user')->andReturn($this->_user); + Auth::shouldReceive('check')->andReturn(true); + $this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id); + $this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($account->email); + + $this->action('GET', 'AccountController@delete',$account->id); + $this->assertResponseOk(); + } + + public function testDestroy() + { + $account = f::create('Account'); + Auth::shouldReceive('user')->andReturn($this->_user); + $this->_repository->shouldReceive('destroy')->once()->with("")->andReturn(true); + $this->action('POST', 'AccountController@destroy',$account->id); + $this->assertRedirectedToRoute('accounts.index'); + $this->assertSessionHas('success'); + } + + public function testDestroyFails() + { + $account = f::create('Account'); + $this->_repository->shouldReceive('destroy')->once()->with("")->andReturn(false); + $this->action('POST', 'AccountController@destroy',$account->id); + $this->assertRedirectedToRoute('accounts.index'); + $this->assertSessionHas('error'); + } + + public function testEdit() + { + $account = f::create('Account'); + + Auth::shouldReceive('user')->andReturn($this->_user); + Auth::shouldReceive('check')->andReturn(true); + $this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id); + $this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($account->email); + $this->_accounts->shouldReceive('openingBalanceTransaction')->once()->andReturn(null); + + $this->action('GET', 'AccountController@edit',$account->id); + $this->assertResponseOk(); + } + + public function testIndex() + { + $account = f::create('Account'); + $collection = new Collection(); + $collection->add($account); + + $list = [ + 'personal' => [], + 'beneficiaries' => [], + 'initial' => [], + 'cash' => [] + ]; + + $this->_repository->shouldReceive('get')->with()->once()->andReturn($collection); + $this->_accounts->shouldReceive('index')->with($collection)->once()->andReturn($list); + $this->action('GET', 'AccountController@index'); + $this->assertResponseOk(); + } + + public function testShow() + { + $account = f::create('Account'); + + Auth::shouldReceive('user')->andReturn($this->_user); + Auth::shouldReceive('check')->andReturn(true); + $this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id); + $this->_user->shouldReceive('getAttribute')->with('email')->once()->andReturn($account->email); + $this->_accounts->shouldReceive('paginate')->with($account,40)->once()->andReturn(); + + $this->action('GET', 'AccountController@show',$account->id); + $this->assertResponseOk(); + } + + public function testStore() + { +// $this->action('POST', 'AccountController@store'); +// $this->assertResponseOk(); + } + + +// +// public function testIndex() +// { +//// // mock account type(s): +//// $personal = $this->mock('AccountType'); +//// $personal->shouldReceive('getAttribute', 'description')->andReturn('Default account'); +//// +//// $bene = $this->mock('AccountType'); +//// $bene->shouldReceive('getAttribute', 'description')->andReturn('Beneficiary account'); +//// +//// $initial = $this->mock('AccountType'); +//// $initial->shouldReceive('getAttribute', 'description')->andReturn('Initial balance account'); +//// +//// $cash = $this->mock('AccountType'); +//// $cash->shouldReceive('getAttribute', 'description')->andReturn('Cash account'); +//// +//// +//// // mock account(s) +//// $one = $this->mock('Account'); +//// $one->shouldReceive('getAttribute')->andReturn($personal); +//// +//// $two = $this->mock('Account'); +//// $two->shouldReceive('getAttribute')->andReturn($bene); +//// +//// $three = $this->mock('Account'); +//// $three->shouldReceive('getAttribute')->andReturn($initial); +//// +//// $four = $this->mock('Account'); +//// $four->shouldReceive('getAttribute')->andReturn($cash); +//// $c = new \Illuminate\Database\Eloquent\Collection([$one, $two, $three, $four]); +//// +//// // mock account repository: +//// $accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface'); +//// $accounts->shouldReceive('get')->andReturn($c); +//// +//// +//// $list = [ +//// 'personal' => [$one], +//// 'beneficiaries' => [$two], +//// 'initial' => [$three], +//// 'cash' => [$four] +//// ]; +//// +//// // mock: +//// View::shouldReceive('share'); +//// View::shouldReceive('make')->with('accounts.index')->once()->andReturn(\Mockery::self()) +//// ->shouldReceive('with')->once()->with('accounts', $list)->andReturn(\Mockery::self()) +//// ->shouldReceive('with')->once()->with('total', 4)->andReturn(\Mockery::self()); +//// +// +// // call +// $this->call('GET', '/accounts'); +// +// // test +// $this->assertResponseOk(); +// +// } +//// +//// public function testCreate() +//// { +//// // mock: +//// View::shouldReceive('share'); +//// View::shouldReceive('make')->with('accounts.create'); +//// +//// // call +//// $this->call('GET', '/accounts/create'); +//// +//// // test +//// $this->assertResponseOk(); +//// } +//// +//// public function testShow() +//// { +//// // mock account repository: +//// $accounts = $this->mock('Firefly\Storage\Account\AccountRepositoryInterface'); +//// $accounts->shouldReceive('get')->with(1)->andReturn([]); +//// +//// // call +//// $this->call('GET', '/accounts/1'); +//// +//// // test +//// $this->assertResponseOk(); +//// } +//// + + public function testUpdate() + { + } } \ No newline at end of file diff --git a/app/views/accounts/index.blade.php b/app/views/accounts/index.blade.php index 4c1bbde8ac..2b662071b1 100644 --- a/app/views/accounts/index.blade.php +++ b/app/views/accounts/index.blade.php @@ -47,9 +47,4 @@ -@stop -@section('scripts') - - - @stop \ No newline at end of file diff --git a/app/views/accounts/show.blade.php b/app/views/accounts/show.blade.php index 0f8a7e34c9..d81d40fb93 100644 --- a/app/views/accounts/show.blade.php +++ b/app/views/accounts/show.blade.php @@ -18,18 +18,96 @@
-

Transactions

+

Summary For selected account and period

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
Expense / incomeTransfers
Out + {{mf($show['statistics']['period']['out'])}} + + + {{mf($show['statistics']['period']['t_out'])}} + +
In + {{mf($show['statistics']['period']['in'])}} + + + {{mf($show['statistics']['period']['t_in'])}} + +
Difference{{mf($show['statistics']['period']['diff'])}}{{mf($show['statistics']['period']['t_diff'])}}
+
+
+ + + + + + + + + + + + + +
Related accounts + @foreach($show['statistics']['accounts'] as $acct) + {{{$acct->name}}} + @endforeach +
Related categories + @foreach($show['statistics']['categories'] as $cat) + {{{$cat->name}}} + @endforeach +
Related budgets + @foreach($show['statistics']['budgets'] as $bud) + {{{$bud->name}}} + @endforeach +
+
+
+ +
+
+ + +

Transactions For selected account and period

+ @include('paginated.transactions',['journals' => $show['journals']])
@stop + +@section('styles') + +@stop + @section('scripts') - - - - - + @stop \ No newline at end of file diff --git a/app/views/budgets/indexByBudget.blade.php b/app/views/budgets/indexByBudget.blade.php index 66a4bfe84a..541b00a49c 100644 --- a/app/views/budgets/indexByBudget.blade.php +++ b/app/views/budgets/indexByBudget.blade.php @@ -3,18 +3,29 @@

Firefly - Budgets and limits + Budgets and envelopes

+

Use budgets to organize and limit your expenses.

+

- These are your budgets and if set, their "limits". Firefly uses an "envelope system" for your - budgets, - which means that for each period of time (for example a month) a virtual "envelope" can be created - containing a certain amount of money. Money spent within a budget is removed from the envelope. + Budgets are groups of expenses that reappear every [period]*. Examples could be "groceries", "bills" or + "drinks with friends". The table below lists all of the budgets you have, if any. + By definition, budgets are an estimated amount + of money, ie. € 400,-. Such an estimation can change over time but should always be set. Budgets + without an actual budget are fairly pointless.

-

- Group by date - Create a limit +

+ Use this page to create or change budgets and the estimated amount of money you think is wise. Pages further ahead + will explain what an "envelope" is in the context of budgeting. +

+

+ * Every month, week, year, etc. +

+ +

@@ -24,7 +35,7 @@ Budget Current envelope(s) -   + Update budget @foreach($budgets as $budget) @@ -50,14 +61,14 @@ {{mf($rep->amount,false)}}
- @if($rep->left() < 0) + @if($rep->left < 0) - {{mf($rep->left(),false)}} + {{mf($rep->left,false)}} @else - {{mf($rep->left(),false)}} + {{mf($rep->left,false)}} @endif
@@ -75,10 +86,12 @@
@endif
- - @if($limit->repeats == 0 || ($limit->repeats == 1 && $index == 0)) - - @endif +
+ + @if($limit->repeats == 0 || ($limit->repeats == 1 && $index == 0)) + + @endif +
@endforeach diff --git a/app/views/budgets/indexByDate.blade.php b/app/views/budgets/indexByDate.blade.php index 78acda1a48..46c9940d2f 100644 --- a/app/views/budgets/indexByDate.blade.php +++ b/app/views/budgets/indexByDate.blade.php @@ -3,64 +3,82 @@

Firefly - Budgets and limits + Budgets and envelopes

+

Use budgets to organize and limit your expenses.

+

- These are your budgets and if set, their "limits". Firefly uses an "envelope system" for your - budgets, - which means that for each period of time (for example a month) a virtual "envelope" can be created - containing a certain amount of money. Money spent within a budget is removed from the envelope. + Budgets are groups of expenses that reappear every [period]*. Examples could be "groceries", "bills" or + "drinks with friends". The table below lists all of the budgets you have, if any. + By definition, budgets are an estimated amount + of money, ie. € 400,-. Such an estimation can change over time but should always be set. Budgets + without an actual budget are fairly pointless. +

+

+ Use this page to create or change budgets and the estimated amount of money you think is wise. Pages further ahead + will explain what an "envelope" is in the context of budgeting. +

+

+ * Every month, week, year, etc.

Group by budget - Create a limit + Create an envelope

-@foreach($reps as $date => $data) + + +@foreach($budgets as $date => $entry)
-

{{$data['date']}}

+

{{$entry['date']}} + Create an envelope for {{$entry['date']}} +

- + - @foreach($data['limitrepetitions'] as $index => $rep) + @foreach($entry['limitrepetitions'] as $index => $repetition) + @endforeach diff --git a/app/views/index.blade.php b/app/views/index.blade.php index e9a7620d04..8a6b7173b9 100644 --- a/app/views/index.blade.php +++ b/app/views/index.blade.php @@ -56,7 +56,7 @@ @foreach($set as $data)

- {{{$data[1]->name}}} + {{{$data[1]->name}}}

@include('transactions.journals-small',['transactions' => $data[0],'account' => $data[1]]) diff --git a/app/views/paginated/transactions.blade.php b/app/views/paginated/transactions.blade.php new file mode 100644 index 0000000000..d65a18727b --- /dev/null +++ b/app/views/paginated/transactions.blade.php @@ -0,0 +1,71 @@ +
BudgetBudget Envelope Left  
- {{{$rep->limit->budget->name}}} - +
+ + +
+
+ + {{{$repetition->limit->budget->name}}} + - {{mf($rep->amount,false)}} + {{mf($repetition->amount,false)}} - @if($rep->left() < 0) + @if($repetition->left < 0) - {{mf($rep->left(),false)}} + {{mf($repetition->left,false)}} @else - {{mf($rep->left(),false)}} + {{mf($repetition->left,false)}} @endif
- - + +
- @if($rep->limit->repeats == 1) + @if($repetition->limit->repeats == 1) auto repeats @endif - Add another limit
+ + + + + + + + + + @foreach($journals as $journal) + + + + + + + + + + + @endforeach +
DateDescriptionAmount (€)FromTo
+ @if($journal->transactiontype->type == 'Withdrawal') + + @endif + @if($journal->transactiontype->type == 'Deposit') + + @endif + @if($journal->transactiontype->type == 'Transfer') + + @endif + @if($journal->transactiontype->type == 'Opening balance') + + @endif + + @foreach($journal->components as $component) + @if($component->class == 'Budget') + + @endif + @if($component->class == 'Category') + + @endif + @endforeach + + {{$journal->date->format('d F Y')}} + {{{$journal->description}}} + @if($journal->transactiontype->type == 'Withdrawal') + {{mf($journal->transactions[1]->amount,false)}} + @endif + @if($journal->transactiontype->type == 'Deposit') + {{mf($journal->transactions[1]->amount,false)}} + @endif + @if($journal->transactiontype->type == 'Transfer') + {{mf($journal->transactions[1]->amount,false)}} + @endif + + {{{$journal->transactions[0]->account->name}}} + + {{{$journal->transactions[1]->account->name}}} + + +
+ +{{$journals->links()}} \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index dbd5a7f7bd..e0b7cc582f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,7 +2,7 @@