From 12ae548dabd17c902a167d9bd9d5bac77239e581 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 16 Jul 2014 21:11:43 +0200 Subject: [PATCH] - Updated transaction controller to need less code for the same work. - Small feedback bug in migration controller - Better database create scripts. - Fixed bug in seed scripts. - Cleanup and fixed sorting in various helpers - Extended some tests to catch changed code. - Created show(journal) and edit(journal) (untested) [skip-ci] --- app/controllers/MigrationController.php | 3 + app/controllers/TransactionController.php | 162 +++++------------ ...4620_create_transaction_journals_table.php | 6 + ...06_27_164836_create_transactions_table.php | 71 ++++---- app/database/seeds/DefaultUserSeeder.php | 3 +- .../Helper/Migration/MigrationHelper.php | 3 +- .../Account/EloquentAccountRepository.php | 10 +- .../EloquentTransactionJournalRepository.php | 83 ++++++--- .../TransactionJournalRepositoryInterface.php | 2 + app/models/TransactionJournal.php | 54 +++--- app/models/User.php | 4 + app/routes.php | 16 +- .../controllers/TransactionControllerTest.php | 59 +++++-- app/views/index.blade.php | 5 +- app/views/partials/menu/shared.blade.php | 6 +- app/views/transactions/create.blade.php | 164 ++++++++++++++++++ app/views/transactions/edit.blade.php | 98 +++++++++++ app/views/transactions/journals.blade.php | 2 +- app/views/transactions/show.blade.php | 77 ++++++++ public/assets/javascript/index.new.js | 25 ++- 20 files changed, 616 insertions(+), 237 deletions(-) create mode 100644 app/views/transactions/create.blade.php create mode 100644 app/views/transactions/edit.blade.php create mode 100644 app/views/transactions/show.blade.php diff --git a/app/controllers/MigrationController.php b/app/controllers/MigrationController.php index dde53ab2e1..cecad119ed 100644 --- a/app/controllers/MigrationController.php +++ b/app/controllers/MigrationController.php @@ -35,6 +35,9 @@ class MigrationController extends BaseController $migration->loadFile($file); if ($migration->validFile()) { $migration->migrate(); + } else { + echo 'Invalid file.'; + exit(); } } } diff --git a/app/controllers/TransactionController.php b/app/controllers/TransactionController.php index 2268295a6c..2ddeccb475 100644 --- a/app/controllers/TransactionController.php +++ b/app/controllers/TransactionController.php @@ -34,26 +34,7 @@ class TransactionController extends BaseController View::share('menu', 'home'); } - /** - * @return $this|\Illuminate\View\View - */ - public function createWithdrawal() - { - - // get accounts with names and id's. - $accounts = $this->_accounts->getActiveDefaultAsSelectList(); - - $budgets = $this->_budgets->getAsSelectList(); - - $budgets[0] = '(no budget)'; - - return View::make('transactions.withdrawal')->with('accounts', $accounts)->with('budgets', $budgets); - } - - /** - * @return $this|\Illuminate\View\View - */ - public function createDeposit() + public function create($what) { // get accounts with names and id's. $accounts = $this->_accounts->getActiveDefaultAsSelectList(); @@ -62,38 +43,39 @@ class TransactionController extends BaseController $budgets[0] = '(no budget)'; - return View::make('transactions.deposit')->with('accounts', $accounts)->with('budgets', $budgets); - + return View::make('transactions.create')->with('accounts', $accounts)->with('budgets', $budgets)->with( + 'what', $what + ); } - /** - * @return $this|\Illuminate\View\View - */ - public function createTransfer() + public function store($what) { - // get accounts with names and id's. - $accounts = $this->_accounts->getActiveDefaultAsSelectList(); + // $fromAccount and $toAccount are found + // depending on the $what - $budgets = $this->_budgets->getAsSelectList(); + $fromAccount = null; + $toAccount = null; - $budgets[0] = '(no budget)'; - - return View::make('transactions.transfer')->with('accounts', $accounts)->with('budgets', $budgets); - - } - - /** - * @return \Illuminate\Http\RedirectResponse - */ - public function postCreateWithdrawal() - { - - // create or find beneficiary: - $beneficiary = $this->_accounts->createOrFindBeneficiary(Input::get('beneficiary')); - - // fall back to cash account if empty: - if (is_null($beneficiary)) { - $beneficiary = $this->_accounts->getCashAccount(); + switch ($what) { + case 'withdrawal': + $fromAccount = $this->_accounts->find(intval(Input::get('account_id'))); + $toAccount = $this->_accounts->createOrFindBeneficiary(Input::get('beneficiary')); + break; + case 'deposit': + $fromAccount = $this->_accounts->createOrFindBeneficiary(Input::get('beneficiary')); + $toAccount = $this->_accounts->find(intval(Input::get('account_id'))); + break; + case 'transfer': + $fromAccount = $this->_accounts->find(intval(Input::get('account_from_id'))); + $toAccount = $this->_accounts->find(intval(Input::get('account_to_id'))); + break; + } + // fall back to cash if necessary: + if (is_null($fromAccount)) { + $fromAccount = $this->_accounts->getCashAccount(); + } + if (is_null($toAccount)) { + $toAccount = $this->_accounts->getCashAccount(); } // create or find category: @@ -102,9 +84,6 @@ class TransactionController extends BaseController // find budget: $budget = $this->_budgets->find(intval(Input::get('budget_id'))); - // find account: - $account = $this->_accounts->find(intval(Input::get('account_id'))); - // find amount & description: $description = trim(Input::get('description')); $amount = floatval(Input::get('amount')); @@ -113,9 +92,9 @@ class TransactionController extends BaseController // create journal /** @var \TransactionJournal $journal */ try { - $journal = $this->_journal->createSimpleJournal($account, $beneficiary, $description, $amount, $date); + $journal = $this->_journal->createSimpleJournal($fromAccount, $toAccount, $description, $amount, $date); } catch (\Firefly\Exception\FireflyException $e) { - return Redirect::route('transactions.withdrawal')->withInput(); + return Redirect::route('transactions.create', $what)->withInput(); } // attach bud/cat (?) @@ -128,80 +107,27 @@ class TransactionController extends BaseController Session::flash('success', 'Transaction saved'); return Redirect::route('index'); + + } - /** - * @return \Illuminate\Http\RedirectResponse - */ - public function postCreateDeposit() + public function show($journalId) { - // create or find beneficiary: - $beneficiary = $this->_accounts->createOrFindBeneficiary(Input::get('beneficiary')); - - // fall back to cash account if empty: - if (is_null($beneficiary)) { - $beneficiary = $this->_accounts->getCashAccount(); + $journal = $this->_journal->find($journalId); + if ($journal) { + return View::make('transactions.show')->with('journal', $journal); } - - // create or find category: - $category = $this->_categories->createOrFind(Input::get('category')); - - // find account: - $account = $this->_accounts->find(intval(Input::get('account_id'))); - - // find amount & description: - $description = trim(Input::get('description')); - $amount = floatval(Input::get('amount')); - $date = new \Carbon\Carbon(Input::get('date')); - - // create journal - /** @var \TransactionJournal $journal */ - try { - $journal = $this->_journal->createSimpleJournal($beneficiary, $account, $description, $amount, $date); - } catch (\Firefly\Exception\FireflyException $e) { - return Redirect::route('transactions.deposit')->withInput(); - } - - if (!is_null($category)) { - $journal->categories()->save($category); - } - - Session::flash('success', 'Transaction saved'); - return Redirect::route('index'); + return View::make('error')->with('message', 'Invalid journal'); } - /** - * @return \Illuminate\Http\RedirectResponse - */ - public function postCreateTransfer() + public function edit($journalId) { - // create or find category: - $category = $this->_categories->createOrFind(Input::get('category')); - - // find account to: - $toAccount = $this->_accounts->find(intval(Input::get('account_to_id'))); - - // find account from - $from = $this->_accounts->find(intval(Input::get('account_from_id'))); - - // find amount & description: - $description = trim(Input::get('description')); - $amount = floatval(Input::get('amount')); - $date = new \Carbon\Carbon(Input::get('date')); - - // create journal - /** @var \TransactionJournal $journal */ - try { - $journal = $this->_journal->createSimpleJournal($from, $toAccount, $description, $amount, $date); - } catch (\Firefly\Exception\FireflyException $e) { - return Redirect::route('transactions.transfer')->withInput(); + $journal = $this->_journal->find($journalId); + if ($journal) { + $accounts = $this->_accounts->getActiveDefaultAsSelectList(); + return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts); } - if (!is_null($category)) { - $journal->categories()->save($category); - } - - Session::flash('success', 'Transaction saved'); - return Redirect::route('index'); + return View::make('error')->with('message', 'Invalid journal'); } } \ No newline at end of file diff --git a/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php b/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php index d5890a813c..794cb32764 100644 --- a/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php +++ b/app/database/migrations/2014_06_27_164620_create_transaction_journals_table.php @@ -16,6 +16,7 @@ class CreateTransactionJournalsTable extends Migration { { $table->increments('id'); $table->timestamps(); + $table->integer('user_id')->unsigned(); $table->integer('transaction_type_id')->unsigned(); $table->integer('transaction_currency_id')->unsigned(); $table->string('description',255)->nullable(); @@ -31,6 +32,11 @@ class CreateTransactionJournalsTable extends Migration { $table->foreign('transaction_currency_id') ->references('id')->on('transaction_currencies') ->onDelete('cascade'); + + // connect users + $table->foreign('user_id') + ->references('id')->on('users') + ->onDelete('cascade'); }); } diff --git a/app/database/migrations/2014_06_27_164836_create_transactions_table.php b/app/database/migrations/2014_06_27_164836_create_transactions_table.php index 2059e93bae..14a4858f76 100644 --- a/app/database/migrations/2014_06_27_164836_create_transactions_table.php +++ b/app/database/migrations/2014_06_27_164836_create_transactions_table.php @@ -1,42 +1,49 @@ increments('id'); - $table->timestamps(); - $table->integer('account_id')->integer(); - $table->integer('transaction_journal_id')->integer()->unsigned(); - $table->string('description',255)->nullable(); - $table->decimal('amount',10,2); + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::create( + 'transactions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('account_id')->unsigned(); + $table->integer('transaction_journal_id')->unsigned(); + $table->string('description', 255)->nullable(); + $table->decimal('amount', 10, 2); - // connect transactions to transaction journals - $table->foreign('transaction_journal_id') - ->references('id')->on('transaction_journals') - ->onDelete('cascade'); + // connect transactions to transaction journals + $table->foreign('transaction_journal_id') + ->references('id')->on('transaction_journals') + ->onDelete('cascade'); - }); - } + // connect account id: + $table->foreign('account_id') + ->references('id')->on('accounts') + ->onDelete('cascade'); - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('transactions'); - } + } + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('transactions'); + } } diff --git a/app/database/seeds/DefaultUserSeeder.php b/app/database/seeds/DefaultUserSeeder.php index 92fa927375..a2fae3ac7a 100644 --- a/app/database/seeds/DefaultUserSeeder.php +++ b/app/database/seeds/DefaultUserSeeder.php @@ -12,9 +12,10 @@ class DefaultUserSeeder extends Seeder 'password' => Hash::make('sander'), 'reset' => null, 'remember_token' => null, - 'migrated' => false + 'migrated' => 0 ] ); + } } \ No newline at end of file diff --git a/app/lib/Firefly/Helper/Migration/MigrationHelper.php b/app/lib/Firefly/Helper/Migration/MigrationHelper.php index c3a9e95b59..f370171720 100644 --- a/app/lib/Firefly/Helper/Migration/MigrationHelper.php +++ b/app/lib/Firefly/Helper/Migration/MigrationHelper.php @@ -74,7 +74,8 @@ class MigrationHelper implements MigrationHelperInterface $cashAT = \AccountType::where('description', 'Cash account')->first(); /** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */ $accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface'); - $cash = $accounts->store(['name' => 'Cash account', 'account_type' => $cashAT, 'active' => false]); + $cash = $accounts->store(['name' => 'Cash account', 'account_type' => $cashAT, 'active' => 0]); + \Log::info('Created cash account (#'.$cash->id.')'); $this->map['cash'] = $cash; } diff --git a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php index 2de96e0792..58ef637918 100644 --- a/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php +++ b/app/lib/Firefly/Storage/Account/EloquentAccountRepository.php @@ -13,7 +13,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface public function get() { - return \Auth::user()->accounts()->with('accounttype')->get(); + return \Auth::user()->accounts()->with('accounttype')->orderBy('name','ASC')->get(); } public function getBeneficiaries() @@ -23,7 +23,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface ) ->where('account_types.description', 'Beneficiary account')->where('accounts.active', 1) - ->get(['accounts.*']); + ->orderBy('accounts.name','ASC')->get(['accounts.*']); return $list; } @@ -34,7 +34,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface public function getByIds($ids) { - return \Auth::user()->accounts()->with('accounttype')->whereIn('id', $ids)->get(); + return \Auth::user()->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name','ASC')->get(); } public function getDefault() @@ -42,7 +42,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface return \Auth::user()->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->where('account_types.description', 'Default account') - ->get(['accounts.*']); + ->orderBy('accounts.name','ASC')->get(['accounts.*']); } public function getActiveDefault() @@ -60,7 +60,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface ) ->where('account_types.description', 'Default account')->where('accounts.active', 1) - ->get(['accounts.*']); + ->orderBy('accounts.name','ASC')->get(['accounts.*']); $return = []; foreach ($list as $entry) { $return[intval($entry->id)] = $entry->name; diff --git a/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php b/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php index c3380ee3b3..5ee1a572cb 100644 --- a/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php +++ b/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php @@ -9,32 +9,52 @@ use Firefly\Exception\FireflyException; class EloquentTransactionJournalRepository implements TransactionJournalRepositoryInterface { + public function find($journalId) + { + return \Auth::user()->transactionjournals()->with( + ['transactions', 'transactioncurrency', 'transactiontype', 'components', 'transactions.account', + 'transactions.account.accounttype'] + ) + ->where('id', $journalId)->first(); + } + /* + + * + */ + + /** + * + * We're building this thinking the money goes from A to B. + * If the amount is negative however, the money still goes + * from A to B but the balances are reversed. + * + * Aka: + * + * Amount = 200 + * A loses 200 (-200). * -1 + * B gains 200 (200). * 1 + * + * Final balance: -200 for A, 200 for B. + * + * When the amount is negative: + * + * Amount = -200 + * A gains 200 (200). * -1 + * B loses 200 (-200). * 1 + * + * @param \Account $from + * @param \Account $to + * @param $description + * @param $amount + * @param \Carbon\Carbon $date + * + * @return \TransactionJournal + * @throws \Firefly\Exception\FireflyException + */ public function createSimpleJournal(\Account $from, \Account $to, $description, $amount, \Carbon\Carbon $date) { - \Log::debug('Creating tranaction "' . $description . '".'); - /* - * We're building this thinking the money goes from A to B. - * If the amount is negative however, the money still goes - * from A to B but the balances are reversed. - * - * Aka: - * - * Amount = 200 - * A loses 200 (-200). * -1 - * B gains 200 (200). * 1 - * - * Final balance: -200 for A, 200 for B. - * - * When the amount is negative: - * - * Amount = -200 - * A gains 200 (200). * -1 - * B loses 200 (-200). * 1 - * - */ - // amounts: $amountFrom = $amount * -1; $amountTo = $amount; @@ -61,10 +81,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito $journalType = \TransactionType::where('type', 'Opening balance')->first(); break; - // both are yours: - case ($fromAT == 'Default account' && $toAT == 'Default account'): - // determin transaction type. If both accounts are new, it's an initial - // balance transfer. + case ($fromAT == 'Default account' && $toAT == 'Default account'): // both are yours: + // determin transaction type. If both accounts are new, it's an initial balance transfer. $journalType = \TransactionType::where('type', 'Transfer')->first(); break; case ($amount < 0): @@ -102,6 +120,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito $journal = new \TransactionJournal(); $journal->transactionType()->associate($journalType); $journal->transactionCurrency()->associate($currency); + $journal->user()->associate(\Auth::user()); $journal->completed = false; $journal->description = $description; $journal->date = $date; @@ -184,7 +203,7 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito // has to be one: if (!isset($journal->transactions[0])) { throw new FireflyException('Journal #' . $journal->id . ' has ' . count($journal->transactions) - . ' transactions!'); + . ' transactions!'); } $transaction = $journal->transactions[0]; $amount = floatval($transaction->amount); @@ -201,6 +220,10 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito } unset($journal, $transaction, $budget, $name, $amount); + + // sort + arsort($result); + return $result; } @@ -233,6 +256,8 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito ) ->after($start)->before($end) ->whereIn('transaction_type_id', $types) + ->orderBy('date', 'DESC') + ->orderBy('id', 'DESC') ->get(['transaction_journals.*']); foreach ($journals as $journal) { foreach ($journal->transactions as $t) { @@ -244,6 +269,10 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito } } } + // sort result: + arsort($result); + + return $result; } diff --git a/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php b/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php index a19bca137e..56ef614561 100644 --- a/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php +++ b/app/lib/Firefly/Storage/TransactionJournal/TransactionJournalRepositoryInterface.php @@ -9,6 +9,8 @@ interface TransactionJournalRepositoryInterface public function get(); + public function find($journalId); + public function getByAccount(\Account $account, $count = 25); public function homeBudgetChart(\Carbon\Carbon $start, \Carbon\Carbon $end); diff --git a/app/models/TransactionJournal.php b/app/models/TransactionJournal.php index d01ed4268a..5be5525ac8 100644 --- a/app/models/TransactionJournal.php +++ b/app/models/TransactionJournal.php @@ -6,32 +6,32 @@ use LaravelBook\Ardent\Ardent; /** * TransactionJournal * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $transaction_type_id - * @property integer $transaction_currency_id - * @property string $description - * @property boolean $completed - * @property \Carbon\Carbon $date - * @property-read \TransactionType $transactionType - * @property-read \TransactionCurrency $transactionCurrency + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property integer $transaction_type_id + * @property integer $transaction_currency_id + * @property string $description + * @property boolean $completed + * @property \Carbon\Carbon $date + * @property-read \TransactionType $transactionType + * @property-read \TransactionCurrency $transactionCurrency * @property-read \Illuminate\Database\Eloquent\Collection|\Transaction[] $transactions - * @property-read \Illuminate\Database\Eloquent\Collection|\Component[] $components + * @property-read \Illuminate\Database\Eloquent\Collection|\Component[] $components * @property-read \Illuminate\Database\Eloquent\Collection|\ * 'Budget[] $budgets * @property-read \Illuminate\Database\Eloquent\Collection|\ * 'Category[] $categories - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereId($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionTypeId($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionCurrencyId($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCompleted($value) - * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDate($value) - * @method static \TransactionJournal after($date) - * @method static \TransactionJournal before($date) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereId($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCreatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereUpdatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionTypeId($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereTransactionCurrencyId($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDescription($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereCompleted($value) + * @method static \Illuminate\Database\Query\Builder|\TransactionJournal whereDate($value) + * @method static \TransactionJournal after($date) + * @method static \TransactionJournal before($date) */ class TransactionJournal extends Ardent { @@ -51,6 +51,7 @@ class TransactionJournal extends Ardent 'transaction_currency_id' => 'factory|TransactionCurrency', 'description' => 'string', 'completed' => '1', + 'user_id' => 'factory|User', 'date' => 'date|Y-m-d' ]; @@ -59,6 +60,17 @@ class TransactionJournal extends Ardent return $this->belongsTo('TransactionType'); } + /** + * User + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user() + { + return $this->belongsTo('User'); + } + + public function transactionCurrency() { return $this->belongsTo('TransactionCurrency'); diff --git a/app/models/User.php b/app/models/User.php index fa90743c6d..75d0cf02e0 100644 --- a/app/models/User.php +++ b/app/models/User.php @@ -91,4 +91,8 @@ class User extends Ardent implements UserInterface, RemindableInterface return $this->hasMany('Category'); } + public function transactionjournals() { + return $this->hasMany('TransactionJournal'); + } + } \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index f3b7ed34cb..ba0d8f8e03 100644 --- a/app/routes.php +++ b/app/routes.php @@ -32,9 +32,13 @@ Route::group(['before' => 'auth'], function () { // transaction controller: - Route::get('/transactions/add/withdrawal', ['uses' => 'TransactionController@createWithdrawal', 'as' => 'transactions.withdrawal']); - Route::get('/transactions/add/deposit', ['uses' => 'TransactionController@createDeposit', 'as' => 'transactions.deposit']); - Route::get('/transactions/add/transfer', ['uses' => 'TransactionController@createTransfer', 'as' => 'transactions.transfer']); + Route::get('/transactions/create/{what}', ['uses' => 'TransactionController@create', 'as' => 'transactions.create']) + ->where(['what' => 'withdrawal|deposit|transfer']); + + + Route::get('/transaction/show/{id}',['uses' => 'TransactionController@show','as' => 'transactions.show']); + Route::get('/transaction/edit/{id}',['uses' => 'TransactionController@edit','as' => 'transactions.edit']); + Route::get('/transaction/delete/{id}',['uses' => 'TransactionController@delete','as' => 'transactions.delete']); // migration controller Route::get('/migrate', ['uses' => 'MigrationController@index', 'as' => 'migrate']); @@ -56,9 +60,9 @@ Route::group(['before' => 'csrf|auth'], function () { Route::get('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']); // transaction controller: - Route::post('/transactions/add/withdrawal', ['uses' => 'TransactionController@postCreateWithdrawal']); - Route::post('/transactions/add/deposit', ['uses' => 'TransactionController@postCreateDeposit']); - Route::post('/transactions/add/transfer', ['uses' => 'TransactionController@postCreateTransfer']); + Route::post('/transactions/store/{what}', ['uses' => 'TransactionController@store', 'as' => 'transactions.store']) + ->where(['what' => 'withdrawal|deposit|transfer']); + Route::post('/transaction/update/{id}',['uses' => 'TransactionController@update','as' => 'transactions.update']); } ); diff --git a/app/tests/controllers/TransactionControllerTest.php b/app/tests/controllers/TransactionControllerTest.php index d4e46d964a..aa770dd2db 100644 --- a/app/tests/controllers/TransactionControllerTest.php +++ b/app/tests/controllers/TransactionControllerTest.php @@ -28,7 +28,10 @@ class TransactionControllerTest extends TestCase $set = [0 => '(no budget)']; View::shouldReceive('share'); - View::shouldReceive('make')->with('transactions.withdrawal')->andReturn(\Mockery::self()) + View::shouldReceive('make')->with('transactions.create')->andReturn(\Mockery::self()) + ->shouldReceive('with')->once() + ->with('what','withdrawal') + ->andReturn(Mockery::self()) ->shouldReceive('with')->once() ->with('accounts', []) ->andReturn(Mockery::self()) @@ -45,7 +48,7 @@ class TransactionControllerTest extends TestCase // call - $this->call('GET', '/transactions/add/withdrawal'); + $this->call('GET', '/transactions/create/withdrawal'); // test $this->assertResponseOk(); @@ -56,7 +59,11 @@ class TransactionControllerTest extends TestCase $set = [0 => '(no budget)']; View::shouldReceive('share'); - View::shouldReceive('make')->with('transactions.deposit')->andReturn(\Mockery::self()) + View::shouldReceive('make')->with('transactions.create')->andReturn(\Mockery::self()) + ->shouldReceive('with')->once() + ->with('what','deposit') + ->andReturn(Mockery::self()) + ->shouldReceive('with')->once() ->with('accounts', []) ->andReturn(Mockery::self()) @@ -73,7 +80,7 @@ class TransactionControllerTest extends TestCase // call - $this->call('GET', '/transactions/add/deposit'); + $this->call('GET', '/transactions/create/deposit'); // test $this->assertResponseOk(); @@ -84,7 +91,10 @@ class TransactionControllerTest extends TestCase $set = [0 => '(no budget)']; View::shouldReceive('share'); - View::shouldReceive('make')->with('transactions.transfer')->andReturn(\Mockery::self()) + View::shouldReceive('make')->with('transactions.create')->andReturn(\Mockery::self()) + ->shouldReceive('with')->once() + ->with('what', 'transfer') + ->andReturn(Mockery::self()) ->shouldReceive('with')->once() ->with('accounts', []) ->andReturn(Mockery::self()) @@ -101,7 +111,7 @@ class TransactionControllerTest extends TestCase // call - $this->call('GET', '/transactions/add/transfer'); + $this->call('GET', '/transactions/create/transfer'); // test $this->assertResponseOk(); @@ -148,7 +158,7 @@ class TransactionControllerTest extends TestCase $tj->shouldReceive('createSimpleJournal')->once()->andReturn($journal); // call - $this->call('POST', '/transactions/add/withdrawal', $data); + $this->call('POST', '/transactions/store/withdrawal', $data); // test $this->assertRedirectedToRoute('index'); @@ -186,8 +196,13 @@ class TransactionControllerTest extends TestCase $tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); $tj->shouldReceive('createSimpleJournal')->once()->andReturn($journal); + // mock budget repository + $budgets = $this->mock('Firefly\Storage\Budget\BudgetRepositoryInterface'); + $budgets->shouldReceive('createOrFind')->with('')->andReturn(null); + $budgets->shouldReceive('find')->andReturn(null); + // call - $this->call('POST', '/transactions/add/deposit', $data); + $this->call('POST', '/transactions/store/deposit', $data); // test $this->assertRedirectedToRoute('index'); @@ -225,8 +240,13 @@ class TransactionControllerTest extends TestCase $tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); $tj->shouldReceive('createSimpleJournal')->once()->andReturn($journal); + // mock budget repository + $budgets = $this->mock('Firefly\Storage\Budget\BudgetRepositoryInterface'); + $budgets->shouldReceive('createOrFind')->with('')->andReturn(null); + $budgets->shouldReceive('find')->andReturn(null); + // call - $this->call('POST', '/transactions/add/transfer', $data); + $this->call('POST', '/transactions/store/transfer', $data); // test $this->assertRedirectedToRoute('index'); @@ -273,7 +293,7 @@ class TransactionControllerTest extends TestCase $tj->shouldReceive('createSimpleJournal')->once()->andReturn($journal); // call - $this->call('POST', '/transactions/add/withdrawal', $data); + $this->call('POST', '/transactions/store/withdrawal', $data); // test $this->assertRedirectedToRoute('index'); @@ -320,7 +340,7 @@ class TransactionControllerTest extends TestCase $tj->shouldReceive('createSimpleJournal')->once()->andReturn($journal); // call - $this->call('POST', '/transactions/add/deposit', $data); + $this->call('POST', '/transactions/store/deposit', $data); // test $this->assertRedirectedToRoute('index'); @@ -370,10 +390,10 @@ class TransactionControllerTest extends TestCase $tj->shouldReceive('createSimpleJournal')->andThrow('Firefly\Exception\FireflyException'); // call - $this->call('POST', '/transactions/add/withdrawal', $data); + $this->call('POST', '/transactions/store/withdrawal', $data); // test - $this->assertRedirectedToRoute('transactions.withdrawal'); + $this->assertRedirectedToRoute('transactions.create',['what' => 'withdrawal']); } /** @@ -420,10 +440,10 @@ class TransactionControllerTest extends TestCase $tj->shouldReceive('createSimpleJournal')->andThrow('Firefly\Exception\FireflyException'); // call - $this->call('POST', '/transactions/add/deposit', $data); + $this->call('POST', '/transactions/store/deposit', $data); // test - $this->assertRedirectedToRoute('transactions.deposit'); + $this->assertRedirectedToRoute('transactions.create',['what' => 'deposit']); } /** @@ -455,15 +475,20 @@ class TransactionControllerTest extends TestCase $categories = $this->mock('Firefly\Storage\Category\CategoryRepositoryInterface'); $categories->shouldReceive('createOrFind')->with($category->name)->andReturn($category); + // mock budget repository + $budgets = $this->mock('Firefly\Storage\Budget\BudgetRepositoryInterface'); + $budgets->shouldReceive('createOrFind')->with('')->andReturn(null); + $budgets->shouldReceive('find')->andReturn(null); + // mock transaction journal: $tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); $tj->shouldReceive('createSimpleJournal')->andThrow('Firefly\Exception\FireflyException'); // call - $this->call('POST', '/transactions/add/transfer', $data); + $this->call('POST', '/transactions/store/transfer', $data); // test - $this->assertRedirectedToRoute('transactions.transfer'); + $this->assertRedirectedToRoute('transactions.create',['what' => 'transfer']); } public function tearDown() diff --git a/app/views/index.blade.php b/app/views/index.blade.php index 28df40c9c3..b838fa5ade 100644 --- a/app/views/index.blade.php +++ b/app/views/index.blade.php @@ -18,10 +18,9 @@ - - + + - diff --git a/app/views/partials/menu/shared.blade.php b/app/views/partials/menu/shared.blade.php index 47771ffa2b..329edfe592 100644 --- a/app/views/partials/menu/shared.blade.php +++ b/app/views/partials/menu/shared.blade.php @@ -4,9 +4,9 @@ Add... diff --git a/app/views/transactions/create.blade.php b/app/views/transactions/create.blade.php new file mode 100644 index 0000000000..8f5ade4321 --- /dev/null +++ b/app/views/transactions/create.blade.php @@ -0,0 +1,164 @@ +@extends('layouts.default') +@section('content') +
+
+

Firefly + Add a new {{$what}} +

+
+
+
+
+

+ Technically speaking, withdrawals, deposits and transfers are all transactions, moving money from + account A to account B. +

+

+ @if($what == 'withdrawal') + A withdrawal is when you spend money on something, moving an amount to a beneficiary. + @endif + @if($what == 'deposit') + A deposit is when you earn money, moving an amount from a beneficiary into your own account. + @endif + @if($what == 'transfer') + TRANSFER + @endif +

+
+
+ +{{Form::open(['class' => 'form-horizontal','url' => route('transactions.store',$what)])}} + +
+
+

Mandatory fields

+ + +
+ +
+ +
+
+ + + @if($what == 'deposit' || $what == 'withdrawal') +
+ +
+ {{Form::select('account_id',$accounts,Input::old('account_id'),['class' => 'form-control'])}} +
+
+ @endif + + + @if($what == 'deposit' || $what == 'withdrawal') +
+ +
+ + This field will auto-complete your existing beneficiaries (if any), but you can type freely to create new ones. +
+
+ @endif + + + @if($what == 'transfer') +
+ +
+ {{Form::select('account_to_id',$accounts,Input::old('account_from_id'),['class' => 'form-control'])}} +
+
+ +
+ +
+ {{Form::select('account_from_id',$accounts,Input::old('account_to_id'),['class' => 'form-control'])}} +
+
+ @endif + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ +
+
+

Optional fields

+ + + @if($what == 'withdrawal') +
+ +
+ {{Form::select('budget_id',$budgets,Input::old('budget_id') ?: 0,['class' => 'form-control'])}} + Select one of your budgets to make this transaction a part of it. +
+
+ @endif + +
+ +
+ + Add more fine-grained information to this transaction by entering a category. + Like the beneficiary-field, this field will auto-complete existing categories but can also be used + to create new ones. + +
+
+ +
+ + + +@stop +@section('scripts') + + +@stop \ No newline at end of file diff --git a/app/views/transactions/edit.blade.php b/app/views/transactions/edit.blade.php new file mode 100644 index 0000000000..d73af6b961 --- /dev/null +++ b/app/views/transactions/edit.blade.php @@ -0,0 +1,98 @@ +@extends('layouts.default') +@section('content') +
+
+

Firefly + Edit transaction "" +

+
+
+
+
+

+ Technically speaking, withdrawals, deposits and transfers are all transactions, moving money from + account A to account B. +

+

+ A deposit is when you earn money, moving an amount from a beneficiary into your own account. +

+
+
+ +{{Form::open(['class' => 'form-horizontal'])}} + +
+
+

Mandatory fields

+ +
+ +
+ +
+
+ + + +
+ +
+ + This field will auto-complete your existing beneficiaries (if any), but you can type freely to create new ones. +
+
+ +
+ +
+ {{Form::select('account_id',$accounts,Input::old('account_id'),['class' => 'form-control'])}} +
+
+ + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+

Optional fields

+ +
+ +
+ + Add more fine-grained information to this transaction by entering a category. + Like the beneficiary-field, this field will auto-complete existing categories but can also be used + to create new ones. + +
+
+ +
+ + + +@stop +@section('scripts') + + +@stop \ No newline at end of file diff --git a/app/views/transactions/journals.blade.php b/app/views/transactions/journals.blade.php index 95bb0d189d..ac551d8cc2 100644 --- a/app/views/transactions/journals.blade.php +++ b/app/views/transactions/journals.blade.php @@ -20,7 +20,7 @@ @endif - {{{$journal->description}}} + {{{$journal->description}}} {{$journal->date->format('jS M Y')}} @foreach($journal->transactions as $t) diff --git a/app/views/transactions/show.blade.php b/app/views/transactions/show.blade.php new file mode 100644 index 0000000000..308555683d --- /dev/null +++ b/app/views/transactions/show.blade.php @@ -0,0 +1,77 @@ +@extends('layouts.default') +@section('content') +
+
+

Firefly + Transaction "{{{$journal->description}}}" +

+
+
+ +
+
+

Metadata

+ + + + + + + + + + + + + + + + + + @foreach($journal->components as $component) + + + + + @endforeach +
Date{{{$journal->date->format('jS F Y')}}}
Currency{{{$journal->transactioncurrency->code}}}
Type{{{$journal->transactiontype->type}}}
Completed + @if($journal->completed == 1) + Yes + @else + No + @endif +
{{$component->class}}{{{$component->name}}}
+
+ +
+

Transactions

+ @foreach($journal->transactions as $t) +

{{{$t->account->name}}}
{{{$t->account->accounttype->description}}}

+ + + + + + @if(!is_null($t->description)) + + + + + @endif +
Amount{{mf($t->amount)}}
Description{{{$t->description}}}
+ @endforeach + +
+
+
+
+ +
+
+ + +@stop +@section('scripts') +@stop \ No newline at end of file diff --git a/public/assets/javascript/index.new.js b/public/assets/javascript/index.new.js index 48975d60cb..e3ab37250b 100644 --- a/public/assets/javascript/index.new.js +++ b/public/assets/javascript/index.new.js @@ -16,16 +16,37 @@ $(function () { title: { text: obj.data('title') }, + yAxis: { + title: { + text: 'Balance (€)' + }, + formatter: function () { + return '$' + Highcharts.numberFormat(this.y, 0); + } + }, xAxis: { - type: 'datetime' + floor: 0, + type: 'datetime', + dateTimeLabelFormats: { + month: '%e %b', + year: '%b' + }, + title: { + text: 'Date' + } }, tooltip: { - valuePrefix: '€ ' + valuePrefix: '€ ', + formatter: function () { + return '€ ' + Highcharts.numberFormat(this.y, 2); + } }, plotOptions: { line: { + negativeColor: '#FF0000', + threshold: 0, lineWidth: 1, marker: { radius: 2