This should complete the account handler.

This commit is contained in:
James Cole 2015-02-14 14:25:29 +01:00
parent 7785ec0222
commit 1d78f98ec8
15 changed files with 494 additions and 9 deletions

View File

@ -0,0 +1,21 @@
<?php namespace FireflyIII\Events;
use FireflyIII\Events\Event;
use Illuminate\Queue\SerializesModels;
class JournalDeleted extends Event {
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@ -0,0 +1,32 @@
<?php namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\JournalDeleted;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
class JournalDeletedHandler {
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param JournalDeleted $event
* @return void
*/
public function handle(JournalDeleted $event)
{
//
}
}

View File

@ -5,6 +5,7 @@ use Carbon\Carbon;
use Config;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\AccountFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Redirect;
use Session;
@ -42,6 +43,63 @@ class AccountController extends Controller
}
/**
* @param Account $account
*
* @return \Illuminate\View\View
*/
public function delete(Account $account)
{
$subTitle = 'Delete ' . strtolower(e($account->accountType->type)) . ' "' . e($account->name) . '"';
return View::make('accounts.delete', compact('account', 'subTitle'));
}
/**
* @param Account $account
*
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Account $account, AccountRepositoryInterface $repository)
{
$type = $account->accountType->type;
$typeName = Config::get('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$repository->destroy($account);
Session::flash('success', 'The ' . e($typeName) . ' account "' . e($name) . '" was deleted.');
return Redirect::route('accounts.index', $typeName);
}
public function edit(Account $account, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = Config::get('firefly.subTitlesByIdentifier.' . $what);
$subTitleIcon = Config::get('firefly.subIconsByIdentifier.' . $what);
$openingBalance = $repository->openingBalanceTransaction($account);
// pre fill some useful values.
// the opening balance is tricky:
$openingBalanceAmount = null;
if ($openingBalance) {
$transaction = $openingBalance->transactions()->where('account_id', $account->id)->first();
$openingBalanceAmount = $transaction->amount;
}
$preFilled = [
'accountRole' => $account->getMeta('accountRole'),
'openingBalanceDate' => $openingBalance ? $openingBalance->date->format('Y-m-d') : null,
'openingBalance' => $openingBalanceAmount
];
Session::flash('preFilled', $preFilled);
return view('accounts.edit', compact('account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what'));
}
/**
* @param string $what
*
@ -84,4 +142,32 @@ class AccountController extends Controller
}
/**
* @param Account $account
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Account $account, AccountFormRequest $request, AccountRepositoryInterface $repository)
{
$what = Config::get('firefly.shortNamesByFullName.' . $account->accountType->type);
$accountData = [
'name' => $request->input('name'),
'active' => $request->input('active'),
'user' => Auth::user()->id,
'accountRole' => $request->input('accountRole'),
'openingBalance' => floatval($request->input('openingBalance')),
'openingBalanceDate' => new Carbon($request->input('openingBalanceDate')),
'openingBalanceCurrency' => intval($request->input('balance_currency_id')),
];
$repository->update($account, $accountData);
Session::flash('success', 'New account "' . $account->name . '" stored!');
return Redirect::route('accounts.index', $what);
}
}

View File

@ -4,6 +4,8 @@ namespace FireflyIII\Http\Requests;
use Auth;
use Config;
use FireflyIII\Models\Account;
use Input;
/**
* Class AccountFormRequest
@ -29,8 +31,13 @@ class AccountFormRequest extends Request
$accountRoles = join(',', array_keys(Config::get('firefly.accountRoles')));
$types = join(',', array_keys(Config::get('firefly.subTitlesByIdentifier')));
$nameRule = 'required|between:1,100|uniqueForUser:accounts,name';
if (Account::find(Input::get('id'))) {
$nameRule = 'required|between:1,100';
}
return [
'name' => 'required|between:1,100|uniqueForUser:accounts,name',
'name' => $nameRule,
'openingBalance' => 'numeric',
'openingBalanceDate' => 'date',
'accountRole' => 'in:' . $accountRoles,

View File

@ -1,4 +1,24 @@
<?php
use FireflyIII\Models\Account;
// models
Route::bind(
'account',
function ($value, $route) {
if (Auth::check()) {
$account = Account::
leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.editable', 1)
->where('accounts.id', $value)
->where('user_id', Auth::user()->id)
->first(['accounts.*']);
if ($account) {
return $account;
}
}
App::abort(404);
}
);
/**
* Home Controller
@ -18,9 +38,10 @@ Route::group(
Route::get('/accounts/edit/{account}', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']);
Route::get('/accounts/delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
Route::get('/accounts/show/{account}/{view?}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
Route::post('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']);
// Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
// Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
Route::post('/accounts/update/{account}', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
/**
* Bills Controller

View File

@ -24,6 +24,23 @@ class Account extends Model
protected $fillable = ['user_id', 'account_type_id', 'name', 'active'];
/**
* @param $fieldName
*
* @return string|null
*/
public function getMeta($fieldName)
{
foreach ($this->accountMeta as $meta) {
if ($meta->name == $fieldName) {
return $meta->data;
}
}
return null;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/

View File

@ -1,6 +1,7 @@
<?php namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
use Watson\Validating\ValidatingTrait;
/**
* Class AccountMeta
@ -10,6 +11,17 @@ use Illuminate\Database\Eloquent\Model;
class AccountMeta extends Model
{
use ValidatingTrait;
protected $rules
= [
'account_id' => 'required|exists:accounts,id',
'name' => 'required|between:1,100',
'data' => 'required'
];
protected $table = 'account_meta';
protected $fillable = ['account_id', 'name', 'data'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@ -11,6 +11,7 @@ use Watson\Validating\ValidatingTrait;
*/
class Transaction extends Model
{
protected $fillable = ['account_id', 'transaction_journal_id', 'description', 'amount'];
protected $rules
= [

View File

@ -188,4 +188,17 @@ class TransactionJournal extends Model
return $this->belongsTo('FireflyIII\User');
}
/**
* @param EloquentBuilder $query
* @param Account $account
*/
public function scopeAccountIs(EloquentBuilder $query, Account $account)
{
if (!isset($this->joinedTransactions)) {
$query->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id');
$this->joinedTransactions = true;
}
$query->where('transactions.account_id', $account->id);
}
}

View File

@ -1,5 +1,8 @@
<?php namespace FireflyIII\Providers;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@ -18,9 +21,12 @@ class EventServiceProvider extends ServiceProvider
*/
protected $listen
= [
'event.name' => [
'event.name' => [
'EventListener',
],
'App\Events\JournalDeleted' => [
'App\Handlers\Events\JournalDeletedHandler@handle',
],
];
/**
@ -34,7 +40,27 @@ class EventServiceProvider extends ServiceProvider
{
parent::boot($events);
//
TransactionJournal::deleted(
function (TransactionJournal $journal) {
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
$transaction->delete();
}
}
);
Account::deleted(
function (Account $account) {
/** @var Transaction $transaction */
foreach ($account->transactions()->get() as $transaction) {
$journal = $transaction->transactionJournal()->first();
$journal->delete();
}
}
);
}
}

View File

@ -5,10 +5,12 @@ namespace FireflyIII\Repositories\Account;
use App;
use Config;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use Log;
/**
* Class AccountRepository
@ -18,6 +20,31 @@ use FireflyIII\Models\TransactionType;
class AccountRepository implements AccountRepositoryInterface
{
/**
* @param Account $account
*
* @return boolean
*/
public function destroy(Account $account)
{
$account->delete();
return true;
}
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function openingBalanceTransaction(Account $account)
{
return TransactionJournal::accountIs($account)
->orderBy('transaction_journals.date', 'ASC')
->orderBy('created_at', 'ASC')
->first(['transaction_journals.*']);
}
/**
* @param array $data
*
@ -26,6 +53,7 @@ class AccountRepository implements AccountRepositoryInterface
public function store(array $data)
{
$newAccount = $this->_store($data);
$this->_storeMetadata($newAccount, $data);
// continue with the opposing account:
@ -46,6 +74,58 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param Account $account
* @param array $data
*/
public function update(Account $account, array $data)
{
// update the account:
$account->name = $data['name'];
$account->active = $data['active'] == '1' ? true : false;
$account->save();
// update meta data:
/** @var AccountMeta $meta */
foreach ($account->accountMeta()->get() as $meta) {
if ($meta->name == 'accountRole') {
$meta->data = $data['accountRole'];
$meta->save();
}
}
$openingBalance = $this->openingBalanceTransaction($account);
// if has openingbalance?
if ($data['openingBalance'] != 0) {
// if opening balance, do an update:
if ($openingBalance) {
// update existing opening balance.
$this->_updateInitialBalance($account, $openingBalance, $data);
} else {
// create new opening balance.
$type = $data['openingBalance'] < 0 ? 'expense' : 'revenue';
$opposingData = [
'user' => $data['user'],
'accountType' => $type,
'name' => $data['name'] . ' initial balance',
'active' => false,
];
$opposing = $this->_store($opposingData);
$this->_storeInitialBalance($account, $opposing, $data);
}
} else {
// opening balance is zero, should we delete it?
if ($openingBalance) {
// delete existing opening balance.
$openingBalance->delete();
}
}
return $account;
}
/**
* @param array $data
*
@ -64,13 +144,38 @@ class AccountRepository implements AccountRepositoryInterface
]
);
if (!$newAccount->isValid()) {
App::abort(500);
// does the account already exist?
$existingAccount = Account::where('user_id', $data['user'])->where('account_type_id', $accountType->id)->where('name', $data['name'])->first();
if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
var_dump($newAccount->getErrors()->toArray());
}
$newAccount = $existingAccount;
}
$newAccount->save();
return $newAccount;
}
/**
* @param Account $account
* @param array $data
*/
protected function _storeMetadata(Account $account, array $data)
{
$metaData = new AccountMeta(
[
'account_id' => $account->id,
'name' => 'accountRole',
'data' => $data['accountRole']
]
);
if (!$metaData->isValid()) {
App::abort(500);
}
$metaData->save();
}
/**
* @param Account $account
* @param Account $opposing
@ -143,4 +248,29 @@ class AccountRepository implements AccountRepositoryInterface
}
/**
* @param Account $account
* @param TransactionJournal $journal
* @param array $data
*
* @return TransactionJournal
*/
protected function _updateInitialBalance(Account $account, TransactionJournal $journal, array $data)
{
$journal->date = $data['openingBalanceDate'];
/** @var Transaction $transaction */
foreach ($journal->transactions()->get() as $transaction) {
if ($account->id == $transaction->account_id) {
$transaction->amount = $data['openingBalance'];
$transaction->save();
}
if ($account->id != $transaction->account_id) {
$transaction->amount = $data['openingBalance'] * -1;
$transaction->save();
}
}
return $journal;
}
}

View File

@ -2,7 +2,8 @@
namespace FireflyIII\Repositories\Account;
use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionJournal;
/**
* Interface AccountRepositoryInterface
*
@ -13,7 +14,31 @@ interface AccountRepositoryInterface
/**
* @param array $data
*
* @return mixed
* @return Account
*/
public function store(array $data);
/**
* @param Account $account
*
* @return boolean
*/
public function destroy(Account $account);
/**
* @param Account $account
* @param array $data
*
* @return Account
*/
public function update(Account $account, array $data);
/**
* @param Account $account
*
* @return TransactionJournal|null
*/
public function openingBalanceTransaction(Account $account);
}

View File

@ -70,10 +70,20 @@ return [
'expense' => ['Expense account', 'Beneficiary account'],
'revenue' => ['Revenue account'],
],
'accountTypeByIdentifier' =>
'accountTypeByIdentifier' =>
[
'asset' => 'Asset account',
'expense' => 'Expense account',
'revenue' => 'Revenue account',
],
'shortNamesByFullName' =>
[
'Default account' => 'asset',
'Asset account' => 'asset',
'Expense account' => 'expense',
'Beneficiary account' => 'expense',
'Revenue account' => 'revenue',
'Cash account' => 'cash',
]
];

View File

@ -0,0 +1,33 @@
@extends('layouts.default')
@section('content')
{{-- Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) --}}
{!! Form::open(['class' => 'form-horizontal','id' => 'destroy','url' => route('accounts.destroy',$account->id)]) !!}
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-red">
<div class="panel-heading">
Delete account "{{{$account->name}}}"
</div>
<div class="panel-body">
<p>
Are you sure?
</p>
@if($account->transactions()->count() > 0)
<p class="text-info">
Account "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(s) associated to it.
These will be deleted as well.
</p>
@endif
<p>
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{URL::previous()}}" class="btn-default btn">Cancel</a >
</p>
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop

View File

@ -0,0 +1,51 @@
@extends('layouts.default')
@section('content')
{{-- Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $account) --}}
{!! Form::model($account, ['class' => 'form-horizontal','id' => 'update','url' => route('accounts.update',$account->id)]) !!}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields
</div>
<div class="panel-body">
{!! ExpandedForm::text('name') !!}
</div>
</div>
<p>
<button type="submit" class="btn btn-lg btn-success">
Update account
</button>
</p>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
</div>
<div class="panel-body">
@if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account')
{!! ExpandedForm::balance('openingBalance',null, ['currency' => $openingBalance ? $openingBalance->transactionCurrency : null]) !!}
{!! ExpandedForm::date('openingBalanceDate') !!}
{!! ExpandedForm::select('accountRole',Config::get('firefly.accountRoles')) !!}
{!! Form::hidden('id',$account->id) !!}
@endif
{!! ExpandedForm::checkbox('active','1') !!}
</div>
</div>
<!-- panel for options -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bolt"></i> Options
</div>
<div class="panel-body">
{!! ExpandedForm::optionsList('update','account') !!}
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop