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( Session::flash('success', 'The account was deleted.');
'message', 'Cannot edit this account type (' . $accountType->description . ').'
);
}
$result = $this->_repository->destroy($account);
if ($result === true) {
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,104 +1,105 @@
<?php <?php
return array( return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Validation Language Lines | Validation Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines contain the default error messages used by | The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such | the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here. | as the size rules. Feel free to tweak each of these messages here.
| |
*/ */
"accepted" => "The :attribute must be accepted.", "accepted" => "The :attribute must be accepted.",
"active_url" => "The :attribute is not a valid URL.", "active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.", "after" => "The :attribute must be a date after :date.",
"alpha" => "The :attribute may only contain letters.", "alpha" => "The :attribute may only contain letters.",
"alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.",
"alpha_num" => "The :attribute may only contain letters and numbers.", "alpha_num" => "The :attribute may only contain letters and numbers.",
"array" => "The :attribute must be an array.", "array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.", "before" => "The :attribute must be a date before :date.",
"between" => array( "between" => array(
"numeric" => "The :attribute must be between :min and :max.", "numeric" => "The :attribute must be between :min and :max.",
"file" => "The :attribute must be between :min and :max kilobytes.", "file" => "The :attribute must be between :min and :max kilobytes.",
"string" => "The :attribute must be between :min and :max characters.", "string" => "The :attribute must be between :min and :max characters.",
"array" => "The :attribute must have between :min and :max items.", "array" => "The :attribute must have between :min and :max items.",
), ),
"confirmed" => "The :attribute confirmation does not match.", "confirmed" => "The :attribute confirmation does not match.",
"date" => "The :attribute is not a valid date.", "date" => "The :attribute is not a valid date.",
"date_format" => "The :attribute does not match the format :format.", "date_format" => "The :attribute does not match the format :format.",
"different" => "The :attribute and :other must be different.", "different" => "The :attribute and :other must be different.",
"digits" => "The :attribute must be :digits digits.", "digits" => "The :attribute must be :digits digits.",
"digits_between" => "The :attribute must be between :min and :max digits.", "digits_between" => "The :attribute must be between :min and :max digits.",
"email" => "The :attribute must be a valid email address.", "email" => "The :attribute must be a valid email address.",
"exists" => "The selected :attribute is invalid.", "exists" => "The selected :attribute is invalid.",
"image" => "The :attribute must be an image.", "image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.", "in" => "The selected :attribute is invalid.",
"integer" => "The :attribute must be an integer.", "integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.", "ip" => "The :attribute must be a valid IP address.",
"max" => array( "max" => array(
"numeric" => "The :attribute may not be greater than :max.", "numeric" => "The :attribute may not be greater than :max.",
"file" => "The :attribute may not be greater than :max kilobytes.", "file" => "The :attribute may not be greater than :max kilobytes.",
"string" => "The :attribute may not be greater than :max characters.", "string" => "The :attribute may not be greater than :max characters.",
"array" => "The :attribute may not have more than :max items.", "array" => "The :attribute may not have more than :max items.",
), ),
"mimes" => "The :attribute must be a file of type: :values.", "mimes" => "The :attribute must be a file of type: :values.",
"min" => array( "min" => array(
"numeric" => "The :attribute must be at least :min.", "numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.", "file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.", "string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.", "array" => "The :attribute must have at least :min items.",
), ),
"not_in" => "The selected :attribute is invalid.", "not_in" => "The selected :attribute is invalid.",
"numeric" => "The :attribute must be a number.", "numeric" => "The :attribute must be a number.",
"regex" => "The :attribute format is invalid.", "regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.", "required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.", "required_if" => "The :attribute field is required when :other is :value.",
"required_with" => "The :attribute field is required when :values is present.", "required_with" => "The :attribute field is required when :values is present.",
"required_with_all" => "The :attribute field is required when :values is present.", "required_with_all" => "The :attribute field is required when :values is present.",
"required_without" => "The :attribute field is required when :values is not present.", "required_without" => "The :attribute field is required when :values is not present.",
"required_without_all" => "The :attribute field is required when none of :values are present.", "required_without_all" => "The :attribute field is required when none of :values are present.",
"same" => "The :attribute and :other must match.", "same" => "The :attribute and :other must match.",
"size" => array( "size" => array(
"numeric" => "The :attribute must be :size.", "numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.", "file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.", "string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.", "array" => "The :attribute must contain :size items.",
), ),
"unique" => "The :attribute has already been taken.", "unique" => "The :attribute has already been taken.",
"url" => "The :attribute format is invalid.", "url" => "The :attribute format is invalid.",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Language Lines | Custom Validation Language Lines
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here you may specify custom validation messages for attributes using the | Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to | convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule. | specify a specific custom language line for a given attribute rule.
| |
*/ */
'custom' => array( 'custom' => array(
'attribute-name' => array( 'attribute-name' => array(
'rule-name' => 'custom-message', 'rule-name' => 'custom-message',
), ),
), ),
'alphabasic' => 'The :attribute field must consist of basic alphanumeric characters.',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Custom Validation Attributes | Custom Validation Attributes
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used to swap attribute place-holders | The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead | with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner. | of "email". This simply helps us make messages a little cleaner.
| |
*/ */
'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.*']);
} }
/** /**
@ -77,7 +35,7 @@ class Account implements AccountInterface
public function show(\Account $account, $perPage) public function show(\Account $account, $perPage)
{ {
$start = \Session::get('start'); $start = \Session::get('start');
$end = \Session::get('end'); $end = \Session::get('end');
$stats = [ $stats = [
'budgets' => [], 'budgets' => [],
'categories' => [], 'categories' => [],
@ -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;
} }
} }
$fromAccount = $item->transactions[0]->account; // since it is entirely possible the database is messed up somehow
$toAccount = $item->transactions[1]->account; // 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;
$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 (!is_null($initialbalanceAccount)) { if (count($accountIDs) > 0) {
$initialbalanceAccount->delete(); // 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();
}
} }
$account->delete(); $account->delete();
/** /**
@ -135,10 +122,11 @@ 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)
->first(); ->where('name', 'like', '%' . $name . '%')
->first();
} }
/** /**
@ -155,9 +143,9 @@ 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.*']);
} }
/** /**
@ -165,12 +153,12 @@ class EloquentAccountRepository implements AccountRepositoryInterface
*/ */
public function getActiveDefaultAsSelectList() public function getActiveDefaultAsSelectList()
{ {
$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 = [];
foreach ($list as $entry) { foreach ($list as $entry) {
$return[intval($entry->id)] = $entry->name; $return[intval($entry->id)] = $entry->name;
@ -185,11 +173,11 @@ class EloquentAccountRepository implements AccountRepositoryInterface
public function getBeneficiaries() public function getBeneficiaries()
{ {
$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.*']);
return $list; return $list;
} }
@ -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,9 +214,9 @@ 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,16 +227,26 @@ 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());
$account->name = $data['name']; $account->name = $data['name'];
$account->active $account->active
= isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval( = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval(
$data['active'] $data['active']
) : 1; ) : 1;
@ -257,7 +255,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
// create initial balance, if necessary: // create initial balance, if necessary:
if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) { if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) {
$amount = floatval($data['openingbalance']); $amount = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']); $date = new Carbon($data['openingbalancedate']);
$this->_createInitialBalance($account, $amount, $date); $this->_createInitialBalance($account, $amount, $date);
} }
} }
@ -286,12 +284,12 @@ 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);
if ($journal) { if ($journal) {
$journal->date = new Carbon($data['openingbalancedate']); $journal->date = new Carbon($data['openingbalancedate']);
$journal->transactions[0]->amount = floatval($data['openingbalance']) * -1; $journal->transactions[0]->amount = floatval($data['openingbalance']) * -1;
$journal->transactions[1]->amount = floatval($data['openingbalance']); $journal->transactions[1]->amount = floatval($data['openingbalance']);
$journal->transactions[0]->save(); $journal->transactions[0]->save();
@ -306,8 +304,8 @@ class EloquentAccountRepository implements AccountRepositoryInterface
/** /**
* @param \Account $account * @param \Account $account
* @param int $amount * @param int $amount
* @param Carbon $date * @param Carbon $date
* *
* @return bool * @return bool
* @SuppressWarnings(PHPMD.CamelCaseMethodName) * @SuppressWarnings(PHPMD.CamelCaseMethodName)
@ -315,24 +313,24 @@ 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;
$initial->accountType()->associate($initialBalanceAT); $initial->accountType()->associate($initialBalanceAT);
$initial->user()->associate(\Auth::user()); $initial->user()->associate(\Auth::user());
$initial->name = $account->name . ' initial balance'; $initial->name = $account->name . ' initial balance';
$initial->active = 0; $initial->active = 0;
if ($initial->validate()) { if ($initial->validate()) {
$initial->save(); $initial->save();
// create new transaction journal (and transactions): // create new transaction journal (and transactions):
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */ /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */
$transactionJournal = \App::make( $transactionJournal = \App::make(
'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface' 'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'
); );
$transactionJournal->createSimpleJournal( $transactionJournal->createSimpleJournal(
$initial, $account, 'Initial Balance for ' . $account->name, $amount, $date $initial, $account, 'Initial Balance for ' . $account->name, $amount, $date
); );
return true; return true;

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'
@ -63,10 +65,10 @@ class Account extends Ardent
return floatval( return floatval(
$this->transactions() $this->transactions()
->leftJoin( ->leftJoin(
'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id' 'transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'
) )
->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount') ->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->sum('transactions.amount')
); );
} }
@ -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

@ -4,18 +4,26 @@
/** /**
* AccountType * AccountType
* *
* @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>
@ -34,9 +37,9 @@
<div class="col-sm-8"> <div class="col-sm-8">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }} {{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }}
@if($errors->has('name')) @if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p> <p class="text-danger">{{$errors->first('name')}}</p>
@else @else
<span class="help-block">Use something descriptive such as "checking account" or "My Bank Main Account".</span> <span class="help-block">Use something descriptive such as "checking account" or "My Bank Main Account".</span>
@endif @endif
</div> </div>

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>