Revamped the account controller.

This commit is contained in:
James Cole 2014-08-30 14:26:33 +02:00
parent 85f1e744b8
commit 9db4137a1b
16 changed files with 339 additions and 519 deletions

View File

@ -5,6 +5,8 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
/** /**
* Class AccountController * Class AccountController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/ */
class AccountController extends \BaseController class AccountController extends \BaseController
{ {
@ -33,41 +35,23 @@ class AccountController extends \BaseController
/** /**
* @param Account $account * @param Account $account
* *
* @return \Illuminate\View\View * @return $this
*/ */
public function delete(Account $account) public function delete(Account $account)
{ {
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return \View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
return View::make('accounts.delete')->with('account', $account); return View::make('accounts.delete')->with('account', $account);
} }
/** /**
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function destroy(Account $account) public function destroy(Account $account)
{ {
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') { $this->_repository->destroy($account);
return View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
$result = $this->_repository->destroy($account);
if ($result === true) {
Session::flash('success', 'The account was deleted.'); Session::flash('success', 'The account was deleted.');
} else {
Session::flash('error', 'Could not delete the account.');
}
return Redirect::route('accounts.index'); return Redirect::route('accounts.index');
@ -76,54 +60,52 @@ class AccountController extends \BaseController
/** /**
* @param Account $account * @param Account $account
* *
* @return \Illuminate\View\View * @return $this
*/ */
public function edit(Account $account) public function edit(Account $account)
{ {
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
$openingBalance = $this->_accounts->openingBalanceTransaction($account); $openingBalance = $this->_accounts->openingBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance); return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance);
} }
/** /**
* @return \Illuminate\View\View * @return $this
*/ */
public function index() public function index()
{ {
$accounts = $this->_repository->get(); $accounts = $this->_repository->get();
$display = $this->_accounts->index($accounts); $set = [
'personal' => [],
'beneficiaries' => []
];
foreach ($accounts as $account) {
switch ($account->accounttype->type) {
case 'Default account':
$set['personal'][] = $account;
break;
case 'Beneficiary account':
$set['beneficiaries'][] = $account;
break;
}
}
return View::make('accounts.index')->with('accounts', $display); return View::make('accounts.index')->with('accounts', $set);
} }
/** /**
* @param Account $account * @param Account $account
* *
* @return \Illuminate\View\View * @return $this
*/ */
public function show(Account $account) public function show(Account $account)
{ {
$accountType = $account->accountType()->first(); $data = $this->_accounts->show($account, 40);
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot show this account type (' . $accountType->description . ').'
);
}
$show = $this->_accounts->show($account, 40); return View::make('accounts.show')->with('account', $account)->with('show', $data);
return View::make('accounts.show')->with('account', $account)->with('show', $show);
} }
/** /**
* @return \Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function store() public function store()
{ {
@ -133,14 +115,14 @@ class AccountController extends \BaseController
if ($account->validate()) { if ($account->validate()) {
// saved! return to wherever. // saved! return to wherever.
Session::flash('success', 'Account "' . $account->name . '" created!'); Session::flash('success', 'Account "' . $account->name . '" created!');
if (Input::get('create') == '1') { if (intval(Input::get('create')) === 1) {
return Redirect::route('accounts.create')->withInput(); return Redirect::route('accounts.create')->withInput();
} else { } else {
return Redirect::route('accounts.index'); return Redirect::route('accounts.index');
} }
} else { } else {
// did not save, return with error: // did not save, return with error:
Session::flash('error', 'Could not save the new account. Please check the form.'); Session::flash('error', 'Could not save the new account: ' . $account->errors()->first());
return Redirect::route('accounts.create')->withErrors($account->errors())->withInput(); return Redirect::route('accounts.create')->withErrors($account->errors())->withInput();
@ -150,16 +132,10 @@ class AccountController extends \BaseController
/** /**
* @param Account $account * @param Account $account
* *
* @return \Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function update(Account $account) public function update(Account $account)
{ {
$accountType = $account->accountType()->first();
if ($accountType->description == 'Initial balance account' || $accountType->description == 'Cash account') {
return View::make('error')->with(
'message', 'Cannot show this account type (' . $accountType->description . ').'
);
}
$account = $this->_repository->update($account, Input::all()); $account = $this->_repository->update($account, Input::all());
if ($account->validate()) { if ($account->validate()) {
Session::flash('success', 'Account "' . $account->name . '" updated.'); Session::flash('success', 'Account "' . $account->name . '" updated.');

View File

@ -22,7 +22,8 @@ class CreateAccountTypesTable extends Migration
'account_types', function (Blueprint $table) { 'account_types', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->timestamps(); $table->timestamps();
$table->string('description', 50); $table->string('type', 50);
$table->boolean('editable');
} }
); );
} }

View File

@ -11,16 +11,16 @@ class AccountTypeSeeder extends Seeder
DB::table('account_types')->delete(); DB::table('account_types')->delete();
AccountType::create( AccountType::create(
['description' => 'Default account'] ['type' => 'Default account','editable' => true]
); );
AccountType::create( AccountType::create(
['description' => 'Cash account'] ['type' => 'Cash account','editable' => false]
); );
AccountType::create( AccountType::create(
['description' => 'Initial balance account'] ['type' => 'Initial balance account','editable' => false]
); );
AccountType::create( AccountType::create(
['description' => 'Beneficiary account'] ['type' => 'Beneficiary account','editable' => true]
); );
} }

View File

@ -1,6 +1,6 @@
<?php <?php
return array( return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -87,6 +87,7 @@ return array(
'rule-name' => 'custom-message', 'rule-name' => 'custom-message',
), ),
), ),
'alphabasic' => 'The :attribute field must consist of basic alphanumeric characters.',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -101,4 +102,4 @@ return array(
'attributes' => array(), 'attributes' => array(),
); ];

View File

@ -2,7 +2,7 @@
namespace Firefly\Helper\Controllers; namespace Firefly\Helper\Controllers;
use Illuminate\Database\Eloquent\Collection; use Firefly\Exception\FireflyException;
/** /**
* Class Account * Class Account
@ -11,43 +11,6 @@ use Illuminate\Database\Eloquent\Collection;
*/ */
class Account implements AccountInterface class Account implements AccountInterface
{ {
/**
* @param Collection $accounts
*
* @return array|mixed
*/
public function index(Collection $accounts)
{
$list = [
'personal' => [],
'beneficiaries' => [],
'initial' => [],
'cash' => []
];
foreach ($accounts as $account) {
switch ($account->accounttype->description) {
case 'Default account':
$list['personal'][] = $account;
break;
case 'Cash account':
$list['cash'][] = $account;
break;
case 'Initial balance account':
$list['initial'][] = $account;
break;
case 'Beneficiary account':
$list['beneficiaries'][] = $account;
break;
}
}
return $list;
}
/** /**
* @param \Account $account * @param \Account $account
* *
@ -55,17 +18,12 @@ class Account implements AccountInterface
*/ */
public function openingBalanceTransaction(\Account $account) public function openingBalanceTransaction(\Account $account)
{ {
$transactionType = \TransactionType::where('type', 'Opening balance')->first();
return \TransactionJournal:: return \TransactionJournal::
with( withRelevantData()->account($account)
['transactions' => function ($q) { ->leftJoin('transaction_types', 'transaction_types.id', '=',
$q->orderBy('amount', 'ASC'); 'transaction_journals.transaction_type_id')
}] ->where('transaction_types.type', 'Opening balance')
)->where('transaction_type_id', $transactionType->id) ->first(['transaction_journals.*']);
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)->first(['transaction_journals.*']);
} }
/** /**
@ -87,29 +45,51 @@ class Account implements AccountInterface
// build a query: // build a query:
$query = \TransactionJournal::with( $query = \TransactionJournal::withRelevantData()->defaultSorting()->account($account)->after($start)
['transactions' => function ($q) { ->before($end);
$q->orderBy('amount', 'ASC'); // filter some:
}, 'transactiontype', 'components' => function ($q) { if (\Input::get('type')) {
$q->orderBy('class'); switch (\Input::get('type')) {
}, 'transactions.account.accounttype'] case 'transactions':
)->orderBy('date', 'DESC')->leftJoin( $query->transactionTypes(['Deposit', 'Withdrawal']);
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id' break;
)->where('transactions.account_id', $account->id)->where('date', '>=', $start->format('Y-m-d'))->where( case 'transfers':
'date', '<=', $end->format('Y-m-d') $query->transactionTypes(['Transfer']);
)->orderBy('transaction_journals.id', 'DESC'); break;
default:
throw new FireflyException('No case for type "' . \Input::get('type') . '"!');
break;
}
}
if (\Input::get('show')) {
switch (\Input::get('show')) {
case 'expenses':
case 'out':
$query->lessThan(0);
break;
case 'income':
case 'in':
$query->moreThan(0);
break;
default:
throw new FireflyException('No case for show "' . \Input::get('show') . '"!');
break;
}
}
// build paginator: // build paginator:
$totalItems = $query->count(); $totalItems = $query->count();
$page = intval(\Input::get('page')) > 1 ? intval(\Input::get('page')) : 1; $page = max(1, intval(\Input::get('page')));
$skip = ($page - 1) * $perPage; $skip = ($page - 1) * $perPage;
$result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']); $result = $query->skip($skip)->take($perPage)->get(['transaction_journals.*']);
// in the mean time, build list of categories, budgets and other accounts:
// get the relevant budgets, categories and accounts from this list:
/** @var $item \TransactionJournal */ /** @var $item \TransactionJournal */
foreach ($result as $item) { foreach ($result as $index => $item) {
$items[] = $item;
foreach ($item->components as $component) { foreach ($item->components as $component) {
if ($component->class == 'Budget') { if ($component->class == 'Budget') {
$stats['budgets'][$component->id] = $component; $stats['budgets'][$component->id] = $component;
@ -118,59 +98,56 @@ class Account implements AccountInterface
$stats['categories'][$component->id] = $component; $stats['categories'][$component->id] = $component;
} }
} }
// since it is entirely possible the database is messed up somehow
// it might be that a transaction journal has only one transaction.
// this is mainly caused by wrong deletions and other artefacts from the past.
// if it is the case, we remove $item and continue like nothing ever happened.
// this will however, mess up some statisics but we can live with that.
// we might be needing some cleanup routine in the future.
// for now, we simply warn the user of this.
if (count($item->transactions) < 2) {
\Session::flash('warning',
'Some transactions are incomplete; they will not be shown. Statistics may differ.');
unset($result[$index]);
continue;
}
$items[] = $item;
$fromAccount = $item->transactions[0]->account; $fromAccount = $item->transactions[0]->account;
$toAccount = $item->transactions[1]->account; $toAccount = $item->transactions[1]->account;
$stats['accounts'][$fromAccount->id] = $fromAccount; $stats['accounts'][$fromAccount->id] = $fromAccount;
$stats['accounts'][$toAccount->id] = $toAccount; $stats['accounts'][$toAccount->id] = $toAccount;
} }
unset($result, $page);
$paginator = \Paginator::make($items, $totalItems, $perPage); $paginator = \Paginator::make($items, $totalItems, $perPage);
unset($result, $page, $item, $fromAccount, $toAccount);
// 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( // statistics (transactions)
\Transaction::where('account_id', $account->id)->where('amount', '<', 0)->leftJoin( $trIn = floatval(\Transaction::before($end)->after($start)->account($account)->moreThan(0)
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' ->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
)->leftJoin( $trOut = floatval(\Transaction::before($end)->after($start)->account($account)->lessThan(0)
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' ->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount'));
)->whereIn('transaction_types.type', ['Deposit', 'Withdrawal'])->where( $trDiff = $trIn + $trOut;
'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( // statistics (transfers)
\Transaction::where('account_id', $account->id)->where('amount', '>', 0)->leftJoin( $trfIn = floatval(\Transaction::before($end)->after($start)->account($account)->moreThan(0)
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' ->transactionTypes(['Transfer'])->sum('transactions.amount'));
)->leftJoin( $trfOut = floatval(\Transaction::before($end)->after($start)->account($account)->lessThan(0)
'transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id' ->transactionTypes(['Transfer'])->sum('transactions.amount'));
)->where('transaction_types.type', 'Transfer')->where( $trfDiff = $trfIn + $trfOut;
'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']; $stats['period'] = [
'in' => $trIn,
'out' => $trOut,
'diff' => $trDiff,
't_in' => $trfIn,
't_out' => $trfOut,
't_diff' => $trfDiff
];
$return = [ $return = [
'journals' => $paginator, 'journals' => $paginator,

View File

@ -12,15 +12,6 @@ use Illuminate\Database\Eloquent\Collection;
interface AccountInterface interface AccountInterface
{ {
/**
* Build the index:
*
* @param Collection $accounts
*
* @return mixed
*/
public function index(Collection $accounts);
/** /**
* @param \Account $account * @param \Account $account
* *

View File

@ -36,7 +36,6 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function createOrFind($name, \AccountType $type = null) public function createOrFind($name, \AccountType $type = null)
{ {
$account = $this->findByName($name, $type); $account = $this->findByName($name, $type);
if (!$account) { if (!$account) {
$data = [ $data = [
@ -60,10 +59,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
if (is_null($name) || strlen($name) == 0) { if (is_null($name) || strlen($name) == 0) {
return null; return null;
} }
$type = \AccountType::where('description', 'Beneficiary account')->first(); $type = \AccountType::where('type', 'Beneficiary account')->first();
/** @noinspection PhpParamsInspection */
return $this->createOrFind($name, $type); return $this->createOrFind($name, $type);
} }
@ -74,38 +70,29 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function destroy(\Account $account) public function destroy(\Account $account)
{ {
// find the oldest transaction which also is a "Opening balance" // find all transaction journals related to this account:
$first = \Transaction:: $journals = \TransactionJournal::withRelevantData()->account($account)->get(['transaction_journals.*']);
leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') $accountIDs = [];
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_journals.user_id', \Auth::user()->id)
->where('transaction_types.type', 'Opening balance')
->where('account_id', '!=', $account->id)
->orderBy('transactions.id', 'DESC')->first(['transactions.*']);
$initialbalanceAccount = null;
if (!is_null($first)) {
$initialbalanceAccount = $first->account()->first();
}
// loop the account, find all transaction journals, and delete them:
$transactions = $account->transactions()->with('transactionjournal')->get();
$journals = [];
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
$journals[$transaction->transaction_journal_id] = $transaction->transactionJournal;
}
/** @var \TransactionJournal $journal */ /** @var \TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
// remember the account id's of the transactions involved:
foreach ($journal->transactions as $t) {
$accountIDs[] = $t->account_id;
}
$journal->delete(); $journal->delete();
}
$accountIDs = array_unique($accountIDs);
if (count($accountIDs) > 0) {
// find the "initial balance" type accounts in this list. Should be just 1.
$query = \Auth::user()->accounts()->accountType(['Initial balance account'])
->whereIn('accounts.id', $accountIDs);
if ($query->count() == 1) {
$iba = $query->first(['accounts.*']);
$iba->delete();
} }
if (!is_null($initialbalanceAccount)) {
$initialbalanceAccount->delete();
} }
$account->delete(); $account->delete();
/** /**
@ -135,9 +122,10 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function findByName($name, \AccountType $type = null) public function findByName($name, \AccountType $type = null)
{ {
$type = is_null($type) ? \AccountType::where('description', 'Default account')->first() : $type; $type = is_null($type) ? \AccountType::where('type', 'Default account')->first() : $type;
return \Auth::user()->accounts()->where('account_type_id', $type->id)->where('name', 'like', '%' . $name . '%') return \Auth::user()->accounts()->where('account_type_id', $type->id)
->where('name', 'like', '%' . $name . '%')
->first(); ->first();
} }
@ -155,7 +143,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
public function getActiveDefault() public function getActiveDefault()
{ {
return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.description', 'Default account')->where('accounts.active', 1) ->where('account_types.type', 'Default account')->where('accounts.active', 1)
->get(['accounts.*']); ->get(['accounts.*']);
} }
@ -168,7 +156,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
$list = \Auth::user()->accounts()->leftJoin( $list = \Auth::user()->accounts()->leftJoin(
'account_types', 'account_types.id', '=', 'accounts.account_type_id' 'account_types', 'account_types.id', '=', 'accounts.account_type_id'
) )
->where('account_types.description', 'Default account')->where('accounts.active', 1) ->where('account_types.type', 'Default account')->where('accounts.active', 1)
->orderBy('accounts.name', 'ASC')->get(['accounts.*']); ->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$return = []; $return = [];
@ -187,7 +175,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
$list = \Auth::user()->accounts()->leftJoin( $list = \Auth::user()->accounts()->leftJoin(
'account_types', 'account_types.id', '=', 'accounts.account_type_id' 'account_types', 'account_types.id', '=', 'accounts.account_type_id'
) )
->where('account_types.description', 'Beneficiary account')->where('accounts.active', 1) ->where('account_types.type', 'Beneficiary account')->where('accounts.active', 1)
->orderBy('accounts.name', 'ASC')->get(['accounts.*']); ->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
@ -213,7 +201,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function getCashAccount() public function getCashAccount()
{ {
$type = \AccountType::where('description', 'Cash account')->first(); $type = \AccountType::where('type', 'Cash account')->first();
$cash = \Auth::user()->accounts()->where('account_type_id', $type->id)->first(); $cash = \Auth::user()->accounts()->where('account_type_id', $type->id)->first();
return $cash; return $cash;
@ -226,7 +214,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
public function getDefault() public function getDefault()
{ {
return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.description', 'Default account') ->where('account_types.type', 'Default account')
->orderBy('accounts.name', 'ASC')->get(['accounts.*']); ->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
} }
@ -239,10 +227,20 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function store($data) public function store($data)
{ {
$defaultAccountType = \AccountType::where('description', 'Default account')->first(); /**
$accountType = isset($data['account_type']) ? $data['account_type'] : $defaultAccountType; * If the AccountType has been passed through, use it:
*/
if (isset($data['account_type']) && is_object($data['account_type'])
&& get_class($data['account_type']) == 'AccountType'
) {
$accountType = $data['account_type'];
} else {
$accountType = \AccountType::where('type', 'Default account')->first();
}
// create Account: /**
* Create new account:
*/
$account = new \Account; $account = new \Account;
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->user()->associate(\Auth::user()); $account->user()->associate(\Auth::user());
@ -286,7 +284,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
/** @var \Firefly\Helper\Controllers\AccountInterface $interface */ /** @var \Firefly\Helper\Controllers\AccountInterface $interface */
$interface = \App::make('Firefly\Helper\Controllers\AccountInterface'); $interface = \App::make('Firefly\Helper\Controllers\AccountInterface');
if ($account->accounttype->description == 'Default account') { if ($account->accounttype->type == 'Default account') {
$journal = $interface->openingBalanceTransaction($account); $journal = $interface->openingBalanceTransaction($account);
@ -315,7 +313,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date) protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date)
{ {
// get account type: // get account type:
$initialBalanceAT = \AccountType::where('description', 'Initial balance account')->first(); $initialBalanceAT = \AccountType::where('type', 'Initial balance account')->first();
// create new account: // create new account:
$initial = new \Account; $initial = new \Account;

View File

@ -64,8 +64,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
} }
// account types for both: // account types for both:
$toAT = $toAccount->accountType->description; $toAT = $toAccount->accountType->type;
$fromAT = $from->accountType->description; $fromAT = $from->accountType->type;
$journalType = null; $journalType = null;
@ -287,6 +287,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$fromAccount = $accountRepository->find(intval($data['account_id'])); $fromAccount = $accountRepository->find(intval($data['account_id']));
$toAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']); $toAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
break; break;
case 'deposit': case 'deposit':
$fromAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']); $fromAccount = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
$toAccount = $accountRepository->find(intval($data['account_id'])); $toAccount = $accountRepository->find(intval($data['account_id']));
@ -295,7 +296,6 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
$fromAccount = $accountRepository->find(intval($data['account_from_id'])); $fromAccount = $accountRepository->find(intval($data['account_from_id']));
$toAccount = $accountRepository->find(intval($data['account_to_id'])); $toAccount = $accountRepository->find(intval($data['account_to_id']));
break; break;
} }
// fall back to cash if necessary: // fall back to cash if necessary:

View File

@ -1,5 +1,6 @@
<?php <?php
use LaravelBook\Ardent\Ardent as Ardent; use LaravelBook\Ardent\Ardent as Ardent;
use LaravelBook\Ardent\Builder;
/** /**
* Account * Account
@ -22,6 +23,7 @@ use LaravelBook\Ardent\Ardent as Ardent;
* @method static \Illuminate\Database\Query\Builder|\Account whereAccountTypeId($value) * @method static \Illuminate\Database\Query\Builder|\Account whereAccountTypeId($value)
* @method static \Illuminate\Database\Query\Builder|\Account whereName($value) * @method static \Illuminate\Database\Query\Builder|\Account whereName($value)
* @method static \Illuminate\Database\Query\Builder|\Account whereActive($value) * @method static \Illuminate\Database\Query\Builder|\Account whereActive($value)
* @method static \Account accountType($types)
*/ */
class Account extends Ardent class Account extends Ardent
{ {
@ -33,7 +35,7 @@ class Account extends Ardent
*/ */
public static $rules public static $rules
= [ = [
'name' => 'required|between:1,100', 'name' => ['required', 'between:1,100', 'alphabasic'],
'user_id' => 'required|exists:users,id', 'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id', 'account_type_id' => 'required|exists:account_types,id',
'active' => 'required|boolean' 'active' => 'required|boolean'
@ -96,7 +98,8 @@ class Account extends Ardent
public function predict( public function predict(
/** @noinspection PhpUnusedParameterInspection */ /** @noinspection PhpUnusedParameterInspection */
\Carbon\Carbon $date \Carbon\Carbon $date
) { )
{
return null; return null;
} }
@ -110,4 +113,13 @@ class Account extends Ardent
return $this->belongsTo('User'); return $this->belongsTo('User');
} }
public function scopeAccountType(Builder $query, array $types) {
if(is_null($this->joinedAccountTypes)) {
$query->leftJoin('account_types','account_types.id','=','accounts.account_type_id');
$this->joinedAccountTypes = true;
}
$query->whereIn('account_types.type',$types);
}
} }

View File

@ -7,15 +7,23 @@
* @property integer $id * @property integer $id
* @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at * @property \Carbon\Carbon $updated_at
* @property string $description * @property string $type
* @property boolean $editable
* @property-read \Illuminate\Database\Eloquent\Collection|\Account[] $accounts * @property-read \Illuminate\Database\Eloquent\Collection|\Account[] $accounts
* @method static \Illuminate\Database\Query\Builder|\AccountType whereId($value) * @method static \Illuminate\Database\Query\Builder|\AccountType whereId($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\AccountType whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|\AccountType whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereDescription($value) * @method static \Illuminate\Database\Query\Builder|\AccountType whereType($value)
* @method static \Illuminate\Database\Query\Builder|\AccountType whereEditable($value)
*/ */
class AccountType extends Eloquent class AccountType extends Eloquent
{ {
public static $rules
= [
'type' => ['required', 'between:1,50', 'alphabasic'],
'editable' => 'required|boolean',
];
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany

View File

@ -57,7 +57,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -72,33 +72,6 @@ class AccountControllerTest extends TestCase
$this->assertViewHas('account'); $this->assertViewHas('account');
$this->assertResponseOk(); $this->assertResponseOk();
} }
/**
* @covers ::delete
*/
public function testDeleteWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
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')->andReturn('some@email');
$this->action('GET', 'AccountController@delete', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
/** /**
* @covers ::destroy * @covers ::destroy
*/ */
@ -109,7 +82,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -126,57 +99,6 @@ class AccountControllerTest extends TestCase
$this->assertSessionHas('success'); $this->assertSessionHas('success');
} }
/**
* @covers ::destroy
*/
public function testDestroyWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->once()->andReturn($this->_user);
Auth::shouldReceive('check')->once()->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->action('POST', 'AccountController@destroy', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
/**
* @covers ::destroy
*/
public function testDestroyFails()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Default account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding:
Auth::shouldReceive('user')->once()->andReturn($this->_user);
Auth::shouldReceive('check')->once()->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('destroy')->once()->andReturn(false);
$this->action('POST', 'AccountController@destroy', $account->id);
$this->assertRedirectedToRoute('accounts.index');
$this->assertSessionHas('error');
}
public function testEdit() public function testEdit()
{ {
@ -185,7 +107,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -208,28 +130,6 @@ class AccountControllerTest extends TestCase
} }
public function testEditWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
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')->andReturn('some@email');
$this->action('GET', 'AccountController@edit', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testIndex() public function testIndex()
{ {
@ -238,7 +138,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -254,7 +154,6 @@ class AccountControllerTest extends TestCase
]; ];
$this->_repository->shouldReceive('get')->once()->andReturn($collection); $this->_repository->shouldReceive('get')->once()->andReturn($collection);
$this->_accounts->shouldReceive('index')->with($collection)->once()->andReturn($list);
$this->action('GET', 'AccountController@index'); $this->action('GET', 'AccountController@index');
$this->assertResponseOk(); $this->assertResponseOk();
} }
@ -266,7 +165,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -303,29 +202,6 @@ class AccountControllerTest extends TestCase
$this->assertResponseOk(); $this->assertResponseOk();
} }
public function testShowWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
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')->andReturn($account->email);
$this->action('GET', 'AccountController@show', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testStore() public function testStore()
{ {
@ -334,7 +210,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -351,7 +227,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -369,7 +245,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -386,7 +262,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();
@ -402,30 +278,6 @@ class AccountControllerTest extends TestCase
} }
public function testUpdateWrongType()
{
/** @var \Account $account */
$account = f::create('Account');
/** @var \AccountType $accountType */
$accountType = f::create('AccountType');
$accountType->description = 'Initial balance account';
$accountType->save();
$account->accountType()->associate($accountType);
$account->save();
// for successful binding.
Auth::shouldReceive('user')->andReturn($this->_user);
Auth::shouldReceive('check')->andReturn(true);
$this->_user->shouldReceive('getAttribute')->with('id')->once()->andReturn($account->user_id);
$this->_repository->shouldReceive('update')->andReturn($account);
$this->action('POST', 'AccountController@update', $account->id);
$this->assertViewHas('message');
$this->assertResponseOk();
}
public function testUpdateFails() public function testUpdateFails()
{ {
/** @var \Account $account */ /** @var \Account $account */
@ -433,7 +285,7 @@ class AccountControllerTest extends TestCase
/** @var \AccountType $accountType */ /** @var \AccountType $accountType */
$accountType = f::create('AccountType'); $accountType = f::create('AccountType');
$accountType->description = 'Default account'; $accountType->type = 'Default account';
$accountType->save(); $accountType->save();
$account->accountType()->associate($accountType); $account->accountType()->associate($accountType);
$account->save(); $account->save();

View File

@ -4,7 +4,7 @@ use League\FactoryMuffin\Facade;
Facade::define( Facade::define(
'AccountType', 'AccountType',
[ [
'description' => function () { 'type' => function () {
$types = [ $types = [
'Default account', 'Default account',
'Cash account', 'Cash account',
@ -13,6 +13,7 @@ Facade::define(
]; ];
return $types[rand(0, 3)]; return $types[rand(0, 3)];
} },
'editable' => 1
] ]
); );

View File

@ -10,11 +10,14 @@
Accounts are the record holders for transactions and transfers. Money moves Accounts are the record holders for transactions and transfers. Money moves
from one account to another. from one account to another.
</p> </p>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12">
<p class="text-info"> <p class="text-info">
In a double-entry bookkeeping system (such as this one) there is a "from" account and a "to" In a double-entry bookkeeping system (such as this one) there is a "from"-account and a "to"-account,
account, even when money is created from thin air (such as interest, or when new accounts already have even when money is created from thin air (such as interest, or when new accounts already have a
a positive balance). positive balance).
</p> </p>
<p class="text-info"><span class="text-danger">This form creates personal accounts only.</span> <p class="text-info"><span class="text-danger">This form creates personal accounts only.</span>

View File

@ -33,7 +33,7 @@
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
@if($account->accounttype->description == 'Default account') @if($account->accounttype->type == 'Default account')
<h4>Optional fields</h4> <h4>Optional fields</h4>
<div class="form-group"> <div class="form-group">

View File

@ -35,22 +35,22 @@
<td>Out</td> <td>Out</td>
<td> <td>
{{mf($show['statistics']['period']['out'])}} {{mf($show['statistics']['period']['out'])}}
<a href="{{route('accounts.show',$account->id)}}#transactions-thisaccount-this-period-expensesonly"><span class="glyphicon glyphicon-circle-arrow-right"></span></a> <a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=expenses"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td> </td>
<td> <td>
{{mf($show['statistics']['period']['t_out'])}} {{mf($show['statistics']['period']['t_out'])}}
<a href="#transactions-thisaccount-this-period-transfers-out-only"><span class="glyphicon glyphicon-circle-arrow-right"></span></a> <a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=out"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>In</td> <td>In</td>
<td> <td>
{{mf($show['statistics']['period']['in'])}} {{mf($show['statistics']['period']['in'])}}
<a href="#transactions-thisaccount-this-period-incomeonly"><span class="glyphicon glyphicon-circle-arrow-right"></span></a> <a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=income"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td> </td>
<td> <td>
{{mf($show['statistics']['period']['t_in'])}} {{mf($show['statistics']['period']['t_in'])}}
<a href="#transactions-thisaccount-this-period-transfers-in-only"><span class="glyphicon glyphicon-circle-arrow-right"></span></a> <a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=in"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -101,7 +101,7 @@
<h4>Transactions <small> For selected account and period</small></h4> <h4>Transactions <small> For selected account and period</small></h4>
@include('paginated.transactions',['journals' => $show['journals']]) @include('paginated.transactions',['journals' => $show['journals'],'sum' => true])
</div> </div>
</div> </div>

View File

@ -50,7 +50,7 @@ $r = Route::current()->getName();
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Create ... <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Create ... <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="{{route('transactions.create','withdrawal')}}" title="For when you spend money"><span class="glyphicon glyphicon-arrow-left"></span> Withdrawal</a></li> <li><a href="{{route('accounts.create')}}" title="Create new account"><span class="glyphicon glyphicon-inbox"></span> Account</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>