From d84d88cc10be3e9bdcea7dc1f737466b2602f3c4 Mon Sep 17 00:00:00 2001 From: Sander Dorigo Date: Tue, 28 Oct 2014 05:58:32 +0100 Subject: [PATCH] Completely revamped the account controller. --- app/controllers/AccountController.php | 424 +++++++++++++++++++------- 1 file changed, 315 insertions(+), 109 deletions(-) diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 2ddcd82e42..a2828c4556 100644 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -1,31 +1,229 @@ _accounts = $accounts; - $this->_repository = $repository; View::share('mainTitleIcon', 'fa-credit-card'); View::share('title', 'Accounts'); } + /** + * @param string $what + * + * @return View + * @throws FireflyException + */ + public function index($what = 'default') + { + switch ($what) { + default: + throw new FireflyException('Cannot handle account type "' . e($what) . '".'); + break; + case 'asset': + View::share('subTitleIcon', 'fa-money'); + View::share('subTitle', 'Asset accounts'); + break; + case 'expense': + View::share('subTitleIcon', 'fa-shopping-cart'); + View::share('subTitle', 'Expense accounts'); + break; + case 'revenue': + View::share('subTitleIcon', 'fa-download'); + View::share('subTitle', 'Revenue accounts'); + break; + } + return View::make('accounts.index')->with('what', $what); + } + + + /** + * @param string $what + * + * @return \Illuminate\Http\JsonResponse + * @throws FireflyException + */ + public function json($what = 'default') + { + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + /** @var \FireflyIII\Shared\Json\Json $json */ + $json = App::make('FireflyIII\Shared\Json\Json'); + + $parameters = $json->dataTableParameters(); + + switch ($what) { + default: + throw new FireflyException('Cannot handle account type "' . e($what) . '".'); + break; + case 'asset': + $accounts = $acct->getAssetAccounts($parameters); + $count = $acct->countAssetAccounts(); + break; + case 'expense': + $accounts = $acct->getExpenseAccounts($parameters); + $count = $acct->countExpenseAccounts(); + break; + case 'revenue': + $accounts = $acct->getRevenueAccounts($parameters); + $count = $acct->countRevenueAccounts(); + break; + } + + /* + * Output the set compatible with data tables: + */ + $return = [ + 'draw' => intval(Input::get('draw')), + 'recordsTotal' => $count, + 'recordsFiltered' => $accounts->count(), + 'data' => [], + ]; + + /* + * Loop the accounts: + */ + /** @var \Account $account */ + foreach ($accounts as $account) { + $entry = [ + 'name' => ['name' => $account->name, 'url' => route('accounts.show', $account->id)], + 'balance' => $account->balance(), + 'id' => [ + 'edit' => route('accounts.edit', $account->id), + 'delete' => route('accounts.delete', $account->id), + ] + ]; + $return['data'][] = $entry; + } + + + return Response::jsoN($return); + } + + public function transactions(Account $account) { + /* + * TODO get the JSON helper to get transactions or something. + */ + + } + + /** + * @param $account + * + * @return \Illuminate\View\View + */ + public function sankey($account) + { + + /* + * Get the stuff. + */ + $start = Session::get('start'); + $end = Session::get('end'); + $query = \TransactionJournal::withRelevantData() + ->defaultSorting() + ->accountIs($account) + ->after($start) + ->before($end); + $set = $query->get(['transaction_journals.*']); + /* + * Arrays we need: + */ + $collection = []; + $filtered = []; + $result = []; + /** @var \TransactionJournal $entry */ + foreach ($set as $entry) { + switch ($entry->transactionType->type) { + case 'Withdrawal': + /** @var Budget $budget */ + $budget = isset($entry->budgets[0]) ? $entry->budgets[0] : null; + $from = $entry->transactions[0]->account->name; + $amount = floatval($entry->transactions[1]->amount); + if ($budget) { + $to = $budget->name; + } else { + $to = '(no budget)'; + } + $collection[] = [$from, $to, $amount]; + + // also make one for the budget: + $from = $to; + $category = $entry->categories()->first(); + if ($category) { + $to = $category->name . ' (cat)'; + } else { + $to = '(no category)'; + } + $collection[] = [$from, $to, $amount]; + break; + } + } + + /* + * To break "cycles", aka money going back AND forth Firefly searches for previously existing + * key sets (in reversed order) and if we find them, fix it. + * + * If the from-to amount found is larger than the amount going back, the amount going back + * is removed and substracted from the current amount. + * + * If the from-to amount found is less than the amount going back, the entry is ignored + * but substracted from the amount going back. + */ + foreach ($collection as $current) { + list($from, $to, $amount) = $current; + $key = $from . $to; + $reversed = $to . $from; + if (!isset($result[$reversed])) { + if (isset($result[$key])) { + $filtered[$key]['amount'] += $amount; + } else { + $filtered[$key] = ['from' => $from, 'to' => $to, 'amount' => $amount]; + } + } else { + /* + * If there is one, see which one will make it: + */ + $otherAmount = $result[$reversed]['amount']; + if ($amount >= $otherAmount) { + unset($result[$reversed]); + $amount = $amount - $otherAmount; + // set: + if (isset($result[$key])) { + $filtered[$key]['amount'] += $amount; + } else { + $filtered[$key] = ['from' => $from, 'to' => $to, 'amount' => $amount]; + } + } else { + $filtered[$reversed]['amount'] -= $amount; + } + } + + } + ksort($filtered); + + /* + * Collect amounts to give the labels the proper + */ + + /* + * Loop it again to add the amounts. + */ + return View::make('accounts.sankey', compact('filtered')); + } + + /** * @return \Illuminate\View\View */ @@ -43,52 +241,7 @@ class AccountController extends \BaseController break; } - - return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with( - 'what', $what - ); - } - - /** - * @return $this - */ - public function asset() - { - View::share('subTitleIcon', 'fa-money'); - - $accounts = $this->_repository->getOfTypes(['Asset account', 'Default account']); - - return View::make('accounts.asset')->with('subTitle', 'Asset accounts')->with( - 'accounts', $accounts - ); - } - - /** - * @return $this - */ - public function expense() - { - View::share('subTitleIcon', 'fa-shopping-cart'); - - $accounts = $this->_repository->getOfTypes(['Expense account', 'Beneficiary account']); - - return View::make('accounts.expense')->with('subTitle', 'Expense accounts')->with( - 'accounts', $accounts - ); - } - - /** - * @return $this - */ - public function revenue() - { - View::share('subTitleIcon', 'fa-download'); - - $accounts = $this->_repository->getOfTypes(['Revenue account']); - - return View::make('accounts.revenue')->with('subTitle', 'Revenue accounts')->with( - 'accounts', $accounts - ); + return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with('what', $what); } /** @@ -99,9 +252,9 @@ class AccountController extends \BaseController public function delete(Account $account) { return View::make('accounts.delete')->with('account', $account) - ->with( - 'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' - ); + ->with( + 'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' + ); } /** @@ -111,20 +264,67 @@ class AccountController extends \BaseController */ public function destroy(Account $account) { + $type = $account->accountType->type; - $this->_repository->destroy($account); + + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); + + /** @var \FireflyIII\Database\TransactionJournal $jrnls */ + $jrnls = App::make('FireflyIII\Database\TransactionJournal'); + + /* + * Find the "initial balance account", should it exist: + */ + $initialBalance = $acct->findInitialBalanceAccount($account); + + /* + * Get all the transaction journals that are part of this/these account(s): + */ + $journals = []; + if ($initialBalance) { + $transactions = $initialBalance->transactions()->get(); + /** @var \Transaction $transaction */ + foreach ($transactions as $transaction) { + $journals[] = $transaction->transaction_journal_id; + } + } + /** @var \Transaction $transaction */ + foreach ($account->transactions() as $transaction) { + $journals[] = $transaction->transaction_journal_id; + } + + $journals = array_unique($journals); + + /* + * Delete the journals. Should get rid of the transactions as well. + */ + foreach ($journals as $id) { + $journal = $jrnls->find($id); + $journal->delete(); + } + + /* + * Delete it + */ + if ($initialBalance) { + $acct->destroy($initialBalance); + } + + $acct->destroy($account); + Session::flash('success', 'The account was deleted.'); switch ($type) { case 'Asset account': case 'Default account': - return Redirect::route('accounts.asset'); + return Redirect::route('accounts.index', 'asset'); break; case 'Expense account': case 'Beneficiary account': - return Redirect::route('accounts.expense'); + return Redirect::route('accounts.index', 'expense'); break; case 'Revenue account': - return Redirect::route('accounts.revenue'); + return Redirect::route('accounts.index', 'revenue'); break; } @@ -153,18 +353,23 @@ class AccountController extends \BaseController break; } - $openingBalance = $this->_accounts->openingBalanceTransaction($account); - return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance) + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); - ->with('subTitle', 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'); - } + $openingBalance = $acct->openingBalanceTransaction($account); + Session::forget('prefilled'); + if (!is_null($openingBalance)) { + $prefilled['openingbalancedate'] = $openingBalance->date->format('Y-m-d'); + $prefilled['openingbalance'] = floatval($openingBalance->transactions()->where('account_id', $account->id)->first()->amount); + Session::flash('prefilled', $prefilled); + } - /** - * @return $this - */ - public function index() - { - return View::make('error')->with('message', 'This view has been disabled'); + + return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)->with( + 'subTitle', 'Edit ' . strtolower( + $account->accountType->type + ) . ' "' . $account->name . '"' + ); } /** @@ -189,54 +394,55 @@ class AccountController extends \BaseController } - $data = $this->_accounts->show($account, 40); - - return View::make('accounts.show')->with('account', $account)->with('show', $data)->with( - 'subTitle', - 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' - ); + //$data = $this->_accounts->show($account, 40); + return View::make('accounts.show') + ->with('account', $account) + ->with('subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'); } /** * @return $this|\Illuminate\Http\RedirectResponse + * @throws FireflyException */ public function store() { $data = Input::all(); $data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset'; + /** @var \FireflyIII\Database\Account $acct */ + $acct = App::make('FireflyIII\Database\Account'); - - switch ($data['what']) { + switch ($data['post_submit_action']) { default: - case 'asset': - $data['account_type'] = 'Asset account'; - break; - case 'expense': - $data['account_type'] = 'Expense account'; - break; - case 'revenue': - $data['account_type'] = 'Revenue account'; + throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"'); break; + case 'create_another': + case 'store': + $messages = $acct->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 account: ' . $messages['errors']->first()); + return Redirect::route('accounts.create', $data['what'])->withInput()->withErrors($messages['errors']); + } + // store! + $acct->store($data); + Session::flash('success', 'New account stored!'); - } - $account = $this->_repository->store($data); - - if ($account->validate()) { - // saved! return to wherever. - Session::flash('success', 'Account "' . $account->name . '" created!'); - if (intval(Input::get('create')) === 1) { + if ($data['post_submit_action'] == 'create_another') { + return Redirect::route('accounts.create', $data['what']); + } else { + return Redirect::route('accounts.index', $data['what']); + } + break; + case 'validate_only': + $messageBags = $acct->validate($data); + Session::flash('warnings', $messageBags['warnings']); + Session::flash('successes', $messageBags['successes']); + Session::flash('errors', $messageBags['errors']); return Redirect::route('accounts.create', $data['what'])->withInput(); - } else { - - return Redirect::route('accounts.' . e($data['what'])); - } - } else { - // did not save, return with error: - Session::flash('error', 'Could not save the new account: ' . $account->errors()->first()); - - return Redirect::route('accounts.create', $data['what'])->withErrors($account->errors())->withInput(); - + break; } }