Merge branch 'feature/account-cleanup' into develop

This commit is contained in:
Sander Dorigo 2014-10-28 09:33:54 +01:00
commit fc91372dd0
32 changed files with 2294 additions and 417 deletions

View File

@ -1,31 +1,116 @@
<?php <?php
use Firefly\Helper\Controllers\AccountInterface as AI; use Firefly\Exception\FireflyException;
use Firefly\Storage\Account\AccountRepositoryInterface as ARI; use Illuminate\Support\MessageBag;
/** /**
* Class AccountController * Class AccountController
*
* @SuppressWarnings(PHPMD.CamelCasePropertyName)
*/ */
class AccountController extends \BaseController class AccountController extends BaseController
{ {
protected $_repository;
protected $_accounts;
/** /**
* @param ARI $repository *
* @param AI $accounts
*/ */
public function __construct(ARI $repository, AI $accounts) public function __construct()
{ {
$this->_accounts = $accounts;
$this->_repository = $repository;
View::share('mainTitleIcon', 'fa-credit-card'); View::share('mainTitleIcon', 'fa-credit-card');
View::share('title', 'Accounts'); View::share('title', 'Accounts');
} }
/**
* @param string $what
*
* @return View
* @throws FireflyException
*/
public function index($what = 'default')
{
switch ($what) {
default:
throw new FireflyException('Cannot handle account type "' . e($what) . '".');
break;
case 'asset':
View::share('subTitleIcon', 'fa-money');
View::share('subTitle', 'Asset accounts');
break;
case 'expense':
View::share('subTitleIcon', 'fa-shopping-cart');
View::share('subTitle', 'Expense accounts');
break;
case 'revenue':
View::share('subTitleIcon', 'fa-download');
View::share('subTitle', 'Revenue accounts');
break;
}
return View::make('accounts.index')->with('what', $what);
}
/**
* @param string $what
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
*/
public function json($what = 'default')
{
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Shared\Json\Json $json */
$json = App::make('FireflyIII\Shared\Json\Json');
$parameters = $json->dataTableParameters();
switch ($what) {
default:
throw new FireflyException('Cannot handle account type "' . e($what) . '".');
break;
case 'asset':
$accounts = $acct->getAssetAccounts($parameters);
$count = $acct->countAssetAccounts();
break;
case 'expense':
$accounts = $acct->getExpenseAccounts($parameters);
$count = $acct->countExpenseAccounts();
break;
case 'revenue':
$accounts = $acct->getRevenueAccounts($parameters);
$count = $acct->countRevenueAccounts();
break;
}
/*
* Output the set compatible with data tables:
*/
$return = [
'draw' => intval(Input::get('draw')),
'recordsTotal' => $count,
'recordsFiltered' => $accounts->count(),
'data' => [],
];
/*
* Loop the accounts:
*/
/** @var \Account $account */
foreach ($accounts as $account) {
$entry = [
'name' => ['name' => $account->name, 'url' => route('accounts.show', $account->id)],
'balance' => $account->balance(),
'id' => [
'edit' => route('accounts.edit', $account->id),
'delete' => route('accounts.delete', $account->id),
]
];
$return['data'][] = $entry;
}
return Response::jsoN($return);
}
/** /**
* @return \Illuminate\View\View * @return \Illuminate\View\View
*/ */
@ -43,52 +128,7 @@ class AccountController extends \BaseController
break; break;
} }
return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with('what', $what);
return View::make('accounts.create')->with('subTitle', 'Create a new ' . $what . ' account')->with(
'what', $what
);
}
/**
* @return $this
*/
public function asset()
{
View::share('subTitleIcon', 'fa-money');
$accounts = $this->_repository->getOfTypes(['Asset account', 'Default account']);
return View::make('accounts.asset')->with('subTitle', 'Asset accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function expense()
{
View::share('subTitleIcon', 'fa-shopping-cart');
$accounts = $this->_repository->getOfTypes(['Expense account', 'Beneficiary account']);
return View::make('accounts.expense')->with('subTitle', 'Expense accounts')->with(
'accounts', $accounts
);
}
/**
* @return $this
*/
public function revenue()
{
View::share('subTitleIcon', 'fa-download');
$accounts = $this->_repository->getOfTypes(['Revenue account']);
return View::make('accounts.revenue')->with('subTitle', 'Revenue accounts')->with(
'accounts', $accounts
);
} }
/** /**
@ -99,9 +139,9 @@ class AccountController extends \BaseController
public function delete(Account $account) public function delete(Account $account)
{ {
return View::make('accounts.delete')->with('account', $account) return View::make('accounts.delete')->with('account', $account)
->with( ->with(
'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"' 'subTitle', 'Delete ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
); );
} }
/** /**
@ -111,20 +151,67 @@ class AccountController extends \BaseController
*/ */
public function destroy(Account $account) public function destroy(Account $account)
{ {
$type = $account->accountType->type; $type = $account->accountType->type;
$this->_repository->destroy($account);
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
/** @var \FireflyIII\Database\TransactionJournal $jrnls */
$jrnls = App::make('FireflyIII\Database\TransactionJournal');
/*
* Find the "initial balance account", should it exist:
*/
$initialBalance = $acct->findInitialBalanceAccount($account);
/*
* Get all the transaction journals that are part of this/these account(s):
*/
$journals = [];
if ($initialBalance) {
$transactions = $initialBalance->transactions()->get();
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
$journals[] = $transaction->transaction_journal_id;
}
}
/** @var \Transaction $transaction */
foreach ($account->transactions() as $transaction) {
$journals[] = $transaction->transaction_journal_id;
}
$journals = array_unique($journals);
/*
* Delete the journals. Should get rid of the transactions as well.
*/
foreach ($journals as $id) {
$journal = $jrnls->find($id);
$journal->delete();
}
/*
* Delete it
*/
if ($initialBalance) {
$acct->destroy($initialBalance);
}
$acct->destroy($account);
Session::flash('success', 'The account was deleted.'); Session::flash('success', 'The account was deleted.');
switch ($type) { switch ($type) {
case 'Asset account': case 'Asset account':
case 'Default account': case 'Default account':
return Redirect::route('accounts.asset'); return Redirect::route('accounts.index', 'asset');
break; break;
case 'Expense account': case 'Expense account':
case 'Beneficiary account': case 'Beneficiary account':
return Redirect::route('accounts.expense'); return Redirect::route('accounts.index', 'expense');
break; break;
case 'Revenue account': case 'Revenue account':
return Redirect::route('accounts.revenue'); return Redirect::route('accounts.index', 'revenue');
break; break;
} }
@ -153,18 +240,23 @@ class AccountController extends \BaseController
break; break;
} }
$openingBalance = $this->_accounts->openingBalanceTransaction($account); /** @var \FireflyIII\Database\Account $acct */
return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance) $acct = App::make('FireflyIII\Database\Account');
->with('subTitle', 'Edit ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'); $openingBalance = $acct->openingBalanceTransaction($account);
} Session::forget('prefilled');
if (!is_null($openingBalance)) {
$prefilled['openingbalancedate'] = $openingBalance->date->format('Y-m-d');
$prefilled['openingbalance'] = floatval($openingBalance->transactions()->where('account_id', $account->id)->first()->amount);
Session::flash('prefilled', $prefilled);
}
/**
* @return $this return View::make('accounts.edit')->with('account', $account)->with('openingBalance', $openingBalance)->with(
*/ 'subTitle', 'Edit ' . strtolower(
public function index() $account->accountType->type
{ ) . ' "' . $account->name . '"'
return View::make('error')->with('message', 'This view has been disabled'); );
} }
/** /**
@ -189,54 +281,55 @@ class AccountController extends \BaseController
} }
$data = $this->_accounts->show($account, 40); //$data = $this->_accounts->show($account, 40);
return View::make('accounts.show')
return View::make('accounts.show')->with('account', $account)->with('show', $data)->with( ->with('account', $account)
'subTitle', ->with('subTitle', 'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"');
'Details for ' . strtolower($account->accountType->type) . ' "' . $account->name . '"'
);
} }
/** /**
* @return $this|\Illuminate\Http\RedirectResponse * @return $this|\Illuminate\Http\RedirectResponse
* @throws FireflyException
*/ */
public function store() public function store()
{ {
$data = Input::all(); $data = Input::all();
$data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset'; $data['what'] = isset($data['what']) && $data['what'] != '' ? $data['what'] : 'asset';
/** @var \FireflyIII\Database\Account $acct */
$acct = App::make('FireflyIII\Database\Account');
switch ($data['post_submit_action']) {
switch ($data['what']) {
default: default:
case 'asset': throw new FireflyException('Cannot handle post_submit_action "' . e($data['post_submit_action']) . '"');
$data['account_type'] = 'Asset account';
break;
case 'expense':
$data['account_type'] = 'Expense account';
break;
case 'revenue':
$data['account_type'] = 'Revenue account';
break; break;
case 'create_another':
case 'store':
$messages = $acct->validate($data);
/** @var MessageBag $messages ['errors'] */
if ($messages['errors']->count() > 0) {
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('error', 'Could not save account: ' . $messages['errors']->first());
return Redirect::route('accounts.create', $data['what'])->withInput()->withErrors($messages['errors']);
}
// store!
$acct->store($data);
Session::flash('success', 'New account stored!');
} if ($data['post_submit_action'] == 'create_another') {
$account = $this->_repository->store($data); return Redirect::route('accounts.create', $data['what']);
} else {
if ($account->validate()) { return Redirect::route('accounts.index', $data['what']);
// saved! return to wherever. }
Session::flash('success', 'Account "' . $account->name . '" created!'); break;
if (intval(Input::get('create')) === 1) { case 'validate_only':
$messageBags = $acct->validate($data);
Session::flash('warnings', $messageBags['warnings']);
Session::flash('successes', $messageBags['successes']);
Session::flash('errors', $messageBags['errors']);
return Redirect::route('accounts.create', $data['what'])->withInput(); return Redirect::route('accounts.create', $data['what'])->withInput();
} else { break;
return Redirect::route('accounts.' . e($data['what']));
}
} else {
// did not save, return with error:
Session::flash('error', 'Could not save the new account: ' . $account->errors()->first());
return Redirect::route('accounts.create', $data['what'])->withErrors($account->errors())->withInput();
} }
} }

View File

@ -29,6 +29,14 @@ class Form
return self::ffInput('checkbox', $name, $value, $options); return self::ffInput('checkbox', $name, $value, $options);
} }
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffAmount($name, $value = null, array $options = []) public static function ffAmount($name, $value = null, array $options = [])
{ {
$options['step'] = 'any'; $options['step'] = 'any';
@ -37,6 +45,21 @@ class Form
} }
/**
* @param $name
* @param null $value
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
}
/** /**
* @param $name * @param $name
* @param null $value * @param null $value

View File

@ -12,14 +12,17 @@ class Account implements AccountInterface
/** /**
* @param \Account $account * @param \Account $account
*
* @return \TransactionJournal|null * @return \TransactionJournal|null
*/ */
public function openingBalanceTransaction(\Account $account) public function openingBalanceTransaction(\Account $account)
{ {
return \TransactionJournal::withRelevantData() return \TransactionJournal::withRelevantData()
->accountIs($account) ->accountIs($account)
->leftJoin('transaction_types', 'transaction_types.id', '=', ->leftJoin(
'transaction_journals.transaction_type_id') 'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance') ->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']); ->first(['transaction_journals.*']);
} }
@ -36,7 +39,7 @@ class Account implements AccountInterface
* For now, Firefly simply warns the user of this. * For now, Firefly simply warns the user of this.
* *
* @param \Account $account * @param \Account $account
* @param $perPage * @param $perPage
* *
* @return array|mixed * @return array|mixed
* @throws \Firefly\Exception\FireflyException * @throws \Firefly\Exception\FireflyException
@ -110,17 +113,25 @@ class Account implements AccountInterface
// statistics (transactions) // statistics (transactions)
$trIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0) $trIn = floatval(
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')); \Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
$trOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0) ->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')); );
$trOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Deposit', 'Withdrawal'])->sum('transactions.amount')
);
$trDiff = $trIn + $trOut; $trDiff = $trIn + $trOut;
// statistics (transfers) // statistics (transfers)
$trfIn = floatval(\Transaction::before($end)->after($start)->accountIs($account)->moreThan(0) $trfIn = floatval(
->transactionTypes(['Transfer'])->sum('transactions.amount')); \Transaction::before($end)->after($start)->accountIs($account)->moreThan(0)
$trfOut = floatval(\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0) ->transactionTypes(['Transfer'])->sum('transactions.amount')
->transactionTypes(['Transfer'])->sum('transactions.amount')); );
$trfOut = floatval(
\Transaction::before($end)->after($start)->accountIs($account)->lessThan(0)
->transactionTypes(['Transfer'])->sum('transactions.amount')
);
$trfDiff = $trfIn + $trfOut; $trfDiff = $trfIn + $trfOut;
$stats['period'] = [ $stats['period'] = [

View File

@ -0,0 +1,426 @@
<?php
namespace FireflyIII\Database;
use Carbon\Carbon;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
use Illuminate\Support\Collection;
/**
* Class Account
*
* @package FireflyIII\Database
*/
class Account implements CUD, CommonDatabaseCalls, AccountInterface
{
use SwitchUser;
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* Get all asset accounts. Optional JSON based parameters.
*
* @param array $parameters
*
* @return Collection
*/
public function getAssetAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Default account', 'Asset account'], $parameters);
}
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account)
{
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat('initial');
return $this->getUser()->accounts()->where('account_type_id', $accountType->id)->where('name', 'LIKE', $account->name . '%')->first();
}
/**
* @param array $types
* @param array $parameters
*
* @return Collection
*/
public function getAccountsByType(array $types, array $parameters = [])
{
/*
* Basic query:
*/
$query = $this->getUser()->accounts()->accountTypeIn($types);
/*
* Without an opening balance, the rest of these queries will fail.
*/
$query->leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id');
$query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
/*
* Not used, but useful for the balance within a certain month / year.
*/
$balanceOnDate = isset($parameters['date']) ? $parameters['date'] : Carbon::now();
$query->where(
function ($q) use ($balanceOnDate) {
$q->where('transaction_journals.date', '<=', $balanceOnDate->format('Y-m-d'));
$q->orWhereNull('transaction_journals.date');
}
);
$query->groupBy('accounts.id');
/*
* If present, process parameters for sorting:
*/
if (isset($parameters['order'])) {
foreach ($parameters['order'] as $instr) {
$query->orderBy($instr['name'], $instr['dir']);
}
}
/*
* If present, process parameters for searching.
*/
if (isset($parameters['search'])) {
$query->where('name', 'LIKE', '%' . e($parameters['search']['value'] . '%'));
}
/*
* If present, start at $start:
*/
if (isset($parameters['start'])) {
$query->skip(intval($parameters['start']));
}
if (isset($parameters['length'])) {
$query->take(intval($parameters['length']));
}
return $query->get(['accounts.*', \DB::Raw('SUM(`transactions`.`amount`) as `balance`')]);
}
/**
* @return int
*/
public function countAssetAccounts()
{
return $this->countAccountsByType(['Default account', 'Asset account']);
}
/**
* @return int
*/
public function countExpenseAccounts()
{
return $this->countAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types)
{
return $this->getUser()->accounts()->accountTypeIn($types)->count();
}
/**
* @param array $parameters
*
* @return Collection
*/
public function getExpenseAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Expense account', 'Beneficiary account'], $parameters);
}
/**
* Get all default accounts.
*
* @return Collection
*/
public function getDefaultAccounts()
{
// TODO: Implement getDefaultAccounts() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts()
{
return $this->countAccountsByType(['Revenue account']);
}
/**
* Get all revenue accounts.
*
* @param array $parameters
*
* @return Collection
*/
public function getRevenueAccounts(array $parameters = [])
{
return $this->getAccountsByType(['Revenue account'], $parameters);
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
$model->delete();
return true;
}
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account)
{
return \TransactionJournal::withRelevantData()
->accountIs($account)
->leftJoin(
'transaction_types', 'transaction_types.id', '=',
'transaction_journals.transaction_type_id'
)
->where('transaction_types.type', 'Opening balance')
->first(['transaction_journals.*']);
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
die('No impl');
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
/*
* Name validation:
*/
if (!isset($model['name'])) {
$errors->add('name', 'Name is mandatory');
}
if (isset($model['name']) && strlen($model['name']) == 0) {
$errors->add('name', 'Name is too short');
}
if (isset($model['name']) && strlen($model['name']) > 100) {
$errors->add('name', 'Name is too long');
}
$validator = \Validator::make([$model], \Account::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
/*
* type validation.
*/
if (!isset($model['what'])) {
$errors->add('name', 'Internal error: need to know type of account!');
}
/*
* Opening balance and opening balance date.
*/
if (isset($model['what']) && $model['what'] == 'asset') {
if (isset($model['openingbalance']) && strlen($model['openingbalance']) > 0 && !is_numeric($model['openingbalance'])) {
$errors->add('openingbalance', 'This is not a number.');
}
if (isset($model['openingbalancedate']) && strlen($model['openingbalancedate']) > 0) {
try {
new Carbon($model['openingbalancedate']);
} catch (\Exception $e) {
$errors->add('openingbalancedate', 'This date is invalid.');
}
}
}
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
if (!$errors->has('openingbalance')) {
$successes->add('openingbalance', 'OK');
}
if (!$errors->has('openingbalancedate')) {
$successes->add('openingbalancedate', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
/*
* Find account type.
*/
/** @var \FireflyIII\Database\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType');
$accountType = $acctType->findByWhat($data['what']);
$data['user_id'] = $this->getUser()->id;
$data['account_type_id'] = $accountType->id;
$data['active'] = isset($data['active']) && $data['active'] === '1' ? 1 : 0;
$data = array_except($data, array('_token', 'what'));
$account = new \Account($data);
if (!$account->validate()) {
var_dump($account->errors()->all());
exit;
}
$account->save();
if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) {
$this->storeInitialBalance($account, $data);
}
/* Tell transaction journal to store a new one.*/
return $account;
}
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data)
{
$opposingData = [
'name' => $account->name . ' Initial Balance',
'active' => 0,
'what' => 'initial'
];
$opposingAccount = $this->store($opposingData);
/*
* Create a journal from opposing to account or vice versa.
*/
$balance = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
/** @var \FireflyIII\Database\TransactionJournal $tj */
$tj = \App::make('FireflyIII\Database\TransactionJournal');
if ($balance < 0) {
// first transaction draws money from the new account to the opposing
$from = $account;
$to = $opposingAccount;
} else {
// first transaction puts money into account
$from = $opposingAccount;
$to = $account;
}
// data for transaction journal:
$balance = $balance < 0 ? $balance * -1 : $balance;
$opening = [
'what' => 'opening',
'currency' => 'EUR',
'amount' => $balance,
'from' => $from,
'to' => $to,
'date' => $date,
'description' => 'Opening balance for new account ' . $account->name,
];
$validation = $tj->validate($opening);
if ($validation['errors']->count() == 0) {
$tj->store($opening);
return true;
} else {
var_dump($validation['errors']);
exit;
}
return false;
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
/**
* Interface AccountInterface
*
* @package FireflyIII\Database
*/
interface AccountInterface
{
/**
* Get all asset accounts. The parameters are optional and are provided by the DataTables plugin.
*
* @param array $parameters
*
* @return Collection
*/
public function getAssetAccounts(array $parameters = []);
/**
* Counts the number of total asset accounts. Useful for DataTables.
*
* @return int
*/
public function countAssetAccounts();
/**
* Counts the number of total expense accounts. Useful for DataTables.
*
* @return int
*/
public function countExpenseAccounts();
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts();
/**
* @param array $parameters
*
* @return Collection
*/
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account);
public function getExpenseAccounts(array $parameters = []);
/**
* Get all revenue accounts.
*
* @param array $parameters
*
* @return Collection
*/
public function getRevenueAccounts(array $parameters = []);
/**
* Get all accounts of the selected types. Is also capable of handling DataTables' parameters.
*
* @param array $types
* @param array $parameters
*
* @return Collection
*/
public function getAccountsByType(array $types, array $parameters = []);
/**
* Counts the number of accounts found with the included types.
*
* @param array $types
*
* @return int
*/
public function countAccountsByType(array $types);
/**
* Get all default accounts.
*
* @return Collection
*/
public function getDefaultAccounts();
/**
* @param \Account $account
*
* @return \TransactionJournal|null
*/
public function openingBalanceTransaction(\Account $account);
/**
* @param \Account $account
* @param array $data
*
* @return bool
*/
public function storeInitialBalance(\Account $account, array $data);
}

View File

@ -0,0 +1,120 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 23/10/14
* Time: 13:40
*/
namespace FireflyIII\Database;
use Firefly\Exception\FireflyException;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
/**
* Class AccountType
*
* @package FireflyIII\Database
*/
class AccountType implements AccountTypeInterface, CUD, CommonDatabaseCalls
{
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
switch ($what) {
case 'expense':
return \AccountType::whereType('Expense account')->first();
break;
case 'asset':
return \AccountType::whereType('Asset account')->first();
break;
case 'revenue':
return \AccountType::whereType('Revenue account')->first();
break;
case 'initial':
return \AccountType::whereType('Initial balance account')->first();
break;
default:
throw new FireflyException('Cannot find account type described as "' . e($what) . '".');
break;
}
return null;
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 23/10/14
* Time: 13:40
*/
namespace FireflyIII\Database;
/**
* Interface AccountTypeInterface
*
* @package FireflyIII\Database
*/
interface AccountTypeInterface
{
}

View File

@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Database;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
/**
* Interface CUD
* @package FireflyIII\Database
*/
interface CUD
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model);
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model);
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model);
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data);
}

View File

@ -0,0 +1,47 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 14/10/14
* Time: 09:31
*/
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
/**
* Interface CommonDatabaseCalls
*
* @package FireflyIII\Database
*/
interface CommonDatabaseCalls
{
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id);
/**
* Returns all objects.
*
* @return Collection
*/
public function get();
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what);
}

View File

@ -0,0 +1,32 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 14/10/14
* Time: 09:45
*/
namespace FireflyIII\Database;
/**
* Class SwitchUser
*
* @package FireflyIII\Database
*/
trait SwitchUser
{
protected $_user;
public function getUser()
{
return $this->_user;
}
/**
* @param $user
*/
public function setUser($user)
{
$this->_user = $user;
}
}

View File

@ -0,0 +1,178 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 11:00
*/
namespace FireflyIII\Database;
use Firefly\Exception\FireflyException;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
/**
* Class Transaction
*
* @package FireflyIII\Database
*/
class Transaction implements TransactionInterface, CUD, CommonDatabaseCalls
{
use SwitchUser;
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (!isset($model['account_id']) && !isset($model['account'])) {
$errors->add('account', 'No account present');
}
if (isset($model['account']) && !($model['account'] instanceof \Account)) {
$errors->add('account', 'No valid account present');
}
if (isset($model['account_id']) && intval($model['account_id']) < 0) {
$errors->add('account', 'No valid account_id present');
}
if (isset($model['piggybank_id']) && intval($model['piggybank_id']) < 0) {
$errors->add('piggybank', 'No valid piggybank_id present');
}
if (!isset($model['transaction_journal_id']) && !isset($model['transaction_journal'])) {
$errors->add('transaction_journal', 'No TJ present');
}
if (isset($model['transaction_journal']) && !($model['transaction_journal'] instanceof \TransactionJournal)) {
$errors->add('transaction_journal', 'No valid transaction_journal present');
}
if (isset($model['transaction_journal_id']) && intval($model['transaction_journal_id']) < 0) {
$errors->add('account', 'No valid transaction_journal_id present');
}
if (isset($model['description']) && strlen($model['description']) > 255) {
$errors->add('account', 'Description too long');
}
if (!isset($model['amount'])) {
$errors->add('amount', 'No amount present.');
}
if (isset($model['amount']) && floatval($model['amount']) == 0) {
$errors->add('amount', 'Invalid amount.');
}
if (!$errors->has('account')) {
$successes->add('account', 'OK');
}
if (!$errors->has('')) {
$successes->add('piggybank', 'OK');
}
if (!$errors->has('transaction_journal')) {
$successes->add('transaction_journal', 'OK');
}
if (!$errors->has('amount')) {
$successes->add('amount', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
$transaction = new \Transaction;
$transaction->account()->associate($data['account']);
$transaction->transactionJournal()->associate($data['transaction_journal']);
$transaction->amount = floatval($data['amount']);
if (isset($data['piggybank'])) {
$transaction->piggybank()->associate($data['piggybank']);
}
if (isset($data['description'])) {
$transaction->description = $data['description'];
}
if ($transaction->validate()) {
$transaction->save();
} else {
throw new FireflyException($transaction->errors()->first());
}
return $transaction;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 10:27
*/
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
/**
* Class TransactionType
*
* @package FireflyIII\Database
*/
class TransactionCurrency implements TransactionCurrencyInterface, CUD, CommonDatabaseCalls
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement get() method.
}
/**
* @param string $code
*
* @return \TransactionCurrency|null
*/
public function findByCode($code)
{
return \TransactionCurrency::whereCode($code)->first();
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 10:28
*/
namespace FireflyIII\Database;
/**
* Interface TransactionTypeInterface
*
* @package FireflyIII\Database
*/
interface TransactionCurrencyInterface
{
/**
* @param string $code
*
* @return \TransactionCurrency|null
*/
public function findByCode($code);
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 13:34
*/
namespace FireflyIII\Database;
/**
* Interface TransactionInterface
*
* @package FireflyIII\Database
*/
interface TransactionInterface
{
}

View File

@ -0,0 +1,253 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 07:16
*/
namespace FireflyIII\Database;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use LaravelBook\Ardent\Ardent;
/**
* Class TransactionJournal
*
* @package FireflyIII\Database
*/
class TransactionJournal implements TransactionJournalInterface, CUD, CommonDatabaseCalls
{
use SwitchUser;
/**
*
*/
public function __construct()
{
$this->setUser(\Auth::user());
}
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (!isset($model['what'])) {
$errors->add('description', 'Internal error: need to know type of transaction!');
}
if (isset($model['recurring_transaction_id']) && intval($model['recurring_transaction_id']) < 0) {
$errors->add('recurring_transaction_id', 'Recurring transaction is invalid.');
}
if (!isset($model['description'])) {
$errors->add('description', 'This field is mandatory.');
}
if (isset($model['description']) && strlen($model['description']) == 0) {
$errors->add('description', 'This field is mandatory.');
}
if (isset($model['description']) && strlen($model['description']) > 255) {
$errors->add('description', 'Description is too long.');
}
if (!isset($model['currency'])) {
$errors->add('description', 'Internal error: currency is mandatory!');
}
if (isset($model['date']) && !($model['date'] instanceof Carbon) && strlen($model['date']) > 0) {
try {
new Carbon($model['date']);
} catch (\Exception $e) {
$errors->add('date', 'This date is invalid.');
}
}
if (!isset($model['date'])) {
$errors->add('date', 'This date is invalid.');
}
if (isset($model['to_id']) && intval($model['to_id']) < 0) {
$errors->add('account_to', 'Invalid to-account');
}
if (isset($model['from_id']) && intval($model['from_id']) < 0) {
$errors->add('account_from', 'Invalid from-account');
}
if (isset($model['to']) && !($model['to'] instanceof \Account)) {
$errors->add('account_to', 'Invalid to-account');
}
if (isset($model['from']) && !($model['from'] instanceof \Account)) {
$errors->add('account_from', 'Invalid from-account');
}
if (!isset($model['amount']) || (isset($model['amount']) && floatval($model['amount']) < 0)) {
$errors->add('amount', 'Invalid amount');
}
if (!isset($model['from']) && !isset($model['to'])) {
$errors->add('account_to', 'No accounts found!');
}
$validator = \Validator::make([$model], \Transaction::$rules);
if ($validator->invalid()) {
$errors->merge($errors);
}
/*
* Add "OK"
*/
if (!$errors->has('description')) {
$successes->add('description', 'OK');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK');
}
return [
'errors' => $errors,
'warnings' => $warnings,
'successes' => $successes
];
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
/** @var \FireflyIII\Database\TransactionType $typeRepository */
$typeRepository = \App::make('FireflyIII\Database\TransactionType');
/** @var \FireflyIII\Database\TransactionCurrency $currencyRepository */
$currencyRepository = \App::make('FireflyIII\Database\TransactionCurrency');
/** @var \FireflyIII\Database\Transaction $transactionRepository */
$transactionRepository = \App::make('FireflyIII\Database\Transaction');
$journalType = $typeRepository->findByWhat($data['what']);
$currency = $currencyRepository->findByCode($data['currency']);
$journal = new \TransactionJournal;
$journal->transactionType()->associate($journalType);
$journal->transactionCurrency()->associate($currency);
$journal->user()->associate($this->getUser());
$journal->description = $data['description'];
$journal->date = $data['date'];
$journal->completed = 0;
//$journal->user_id = $this->getUser()->id;
/*
* This must be enough to store the journal:
*/
if (!$journal->validate()) {
\Log::error($journal->errors()->all());
throw new FireflyException('store() transactionjournal failed, but it should not!');
}
$journal->save();
/*
* Then store both transactions.
*/
$first = [
'account' => $data['from'],
'transaction_journal' => $journal,
'amount' => ($data['amount'] * -1),
];
$validate = $transactionRepository->validate($first);
if ($validate['errors']->count() == 0) {
$transactionRepository->store($first);
} else {
throw new FireflyException($validate['errors']->first());
}
$second = [
'account' => $data['to'],
'transaction_journal' => $journal,
'amount' => floatval($data['amount']),
];
$validate = $transactionRepository->validate($second);
if ($validate['errors']->count() == 0) {
$transactionRepository->store($second);
} else {
throw new FireflyException($validate['errors']->first());
}
$journal->completed = 1;
$journal->save();
return $journal;
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
return $this->getUser()->transactionjournals()->find($id);
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
// TODO: Implement findByWhat() method.
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 07:17
*/
namespace FireflyIII\Database;
/**
* Interface TransactionJournalInterface
*
* @package FireflyIII\Database
*/
interface TransactionJournalInterface
{
}

View File

@ -0,0 +1,111 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 10:27
*/
namespace FireflyIII\Database;
use Illuminate\Support\Collection;
use LaravelBook\Ardent\Ardent;
/**
* Class TransactionType
*
* @package FireflyIII\Database
*/
class TransactionType implements TransactionTypeInterface, CUD, CommonDatabaseCalls
{
/**
* @param Ardent $model
*
* @return bool
*/
public function destroy(Ardent $model)
{
// TODO: Implement destroy() method.
}
/**
* Validates a model. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param Ardent $model
*
* @return array
*/
public function validateObject(Ardent $model)
{
// TODO: Implement validateObject() method.
}
/**
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
* @param array $model
*
* @return array
*/
public function validate(array $model)
{
// TODO: Implement validate() method.
}
/**
* @param array $data
*
* @return Ardent
*/
public function store(array $data)
{
// TODO: Implement store() method.
}
/**
* Returns an object with id $id.
*
* @param int $id
*
* @return Ardent
*/
public function find($id)
{
// TODO: Implement find() method.
}
/**
* Returns all objects.
*
* @return Collection
*/
public function get()
{
// TODO: Implement get() method.
}
/**
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
*/
public function findByWhat($what)
{
switch ($what) {
case 'opening':
return \TransactionType::whereType('Opening balance')->first();
break;
default:
throw new FireflyException('Cannot find transaction type described as "' . e($what) . '".');
break;
}
return null;
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 10:28
*/
namespace FireflyIII\Database;
/**
* Interface TransactionTypeInterface
*
* @package FireflyIII\Database
*/
interface TransactionTypeInterface
{
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 24/10/14
* Time: 08:14
*/
namespace FireflyIII\Exception;
/**
* Class NotImplementedException
*
* @package FireflyIII\Exception
*/
class NotImplementedException extends \Exception
{
}

View File

@ -0,0 +1,14 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 27/10/14
* Time: 09:04
*/
namespace FireflyIII\Shared\Json;
class Account {
}

View File

@ -0,0 +1,86 @@
<?php
namespace FireflyIII\Shared\Json;
/**
* Class Json
* @package FireflyIII\Shared\Json
*/
class Json
{
/**
* Grabs all the parameters entered by the DataTables JQuery plugin and creates
* a nice array to be used by the other methods. It's also cleaning up and what-not.
*
* @return array
*/
public function dataTableParameters()
{
/*
* Process all parameters!
*/
if (intval(\Input::get('length')) < 0) {
$length = 10000; // we get them all if no length is defined.
} else {
$length = intval(\Input::get('length'));
}
$parameters = [
'start' => intval(\Input::get('start')),
'length' => $length,
'draw' => intval(\Input::get('draw')),
];
/*
* Columns:
*/
if (!is_null(\Input::get('columns')) && is_array(\Input::get('columns'))) {
foreach (\Input::get('columns') as $column) {
$parameters['columns'][] = [
'data' => $column['data'],
'name' => $column['name'],
'searchable' => $column['searchable'] == 'true' ? true : false,
'orderable' => $column['orderable'] == 'true' ? true : false,
'search' => [
'value' => $column['search']['value'],
'regex' => $column['search']['regex'] == 'true' ? true : false,
]
];
}
}
/*
* Sorting.
*/
$parameters['orderOnAccount'] = false;
if (!is_null(\Input::get('order')) && is_array(\Input::get('order'))) {
foreach (\Input::get('order') as $order) {
$columnIndex = intval($order['column']);
$columnName = $parameters['columns'][$columnIndex]['name'];
$parameters['order'][] = [
'name' => $columnName,
'dir' => strtoupper($order['dir'])
];
if ($columnName == 'to' || $columnName == 'from') {
$parameters['orderOnAccount'] = true;
}
}
}
/*
* Search parameters:
*/
$parameters['search'] = [
'value' => '',
'regex' => false
];
if (!is_null(\Input::get('search')) && is_array(\Input::get('search'))) {
$search = \Input::get('search');
$parameters['search'] = [
'value' => $search['value'],
'regex' => $search['regex'] == 'true' ? true : false
];
}
return $parameters;
}
}

View File

@ -136,15 +136,12 @@ Route::group(['before' => 'auth'], function () {
Route::get('/jump/{range}',['uses' => 'HomeController@rangeJump','as' => 'rangeJump']); Route::get('/jump/{range}',['uses' => 'HomeController@rangeJump','as' => 'rangeJump']);
// account controller: // account controller:
Route::get('/accounts', ['uses' => 'AccountController@index', 'as' => 'accounts.index']); Route::get('/accounts/json/{what}', ['uses' => 'AccountController@json', 'as' => 'accounts.json'])->where('what','revenue|asset|expense');
Route::get('/accounts/asset', ['uses' => 'AccountController@asset', 'as' => 'accounts.asset']); Route::get('/accounts/{what}', ['uses' => 'AccountController@index', 'as' => 'accounts.index'])->where('what','revenue|asset|expense');
Route::get('/accounts/expense', ['uses' => 'AccountController@expense', 'as' => 'accounts.expense']);
Route::get('/accounts/revenue', ['uses' => 'AccountController@revenue', 'as' => 'accounts.revenue']);
Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as' => 'accounts.create'])->where('what','revenue|asset|expense'); Route::get('/accounts/create/{what}', ['uses' => 'AccountController@create', 'as' => 'accounts.create'])->where('what','revenue|asset|expense');
Route::get('/accounts/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']); Route::get('/accounts/edit/{account}',['uses' => 'AccountController@edit','as' => 'accounts.edit']);
Route::get('/accounts/{account}/edit', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']); Route::get('/accounts/delete/{account}',['uses' => 'AccountController@delete','as' => 'accounts.delete']);
Route::get('/accounts/{account}/delete', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']); Route::get('/accounts/show/{account}',['uses' => 'AccountController@show','as' => 'accounts.show']);
// budget controller: // budget controller:
Route::get('/budgets/date',['uses' => 'BudgetController@indexByDate','as' => 'budgets.index.date']); Route::get('/budgets/date',['uses' => 'BudgetController@indexByDate','as' => 'budgets.index.date']);

View File

@ -72,30 +72,51 @@ App::down(
); );
// forms: // forms:
\Form::macro('ffText', function ($name, $value = null, array $options = []) { \Form::macro(
return \Firefly\Form\Form::ffText($name, $value, $options); 'ffText', function ($name, $value = null, array $options = []) {
}); return \Firefly\Form\Form::ffText($name, $value, $options);
\Form::macro('ffSelect', function ($name, array $list = [], $selected = null, array $options = []) { }
return \Firefly\Form\Form::ffSelect($name, $list, $selected, $options); );
}); \Form::macro(
\Form::macro('ffInteger', function ($name, $value = null, array $options = []) { 'ffSelect', function ($name, array $list = [], $selected = null, array $options = []) {
return \Firefly\Form\Form::ffInteger($name, $value, $options); return \Firefly\Form\Form::ffSelect($name, $list, $selected, $options);
}); }
\Form::macro('ffAmount', function ($name, $value = null, array $options = []) { );
return \Firefly\Form\Form::ffAmount($name, $value, $options); \Form::macro(
}); 'ffInteger', function ($name, $value = null, array $options = []) {
\Form::macro('ffDate', function ($name, $value = null, array $options = []) { return \Firefly\Form\Form::ffInteger($name, $value, $options);
return \Firefly\Form\Form::ffDate($name, $value, $options); }
}); );
\Form::macro('ffTags', function ($name, $value = null, array $options = []) { \Form::macro(
return \Firefly\Form\Form::ffTags($name, $value, $options); 'ffAmount', function ($name, $value = null, array $options = []) {
}); return \Firefly\Form\Form::ffAmount($name, $value, $options);
\Form::macro('ffCheckbox',function ($name, $value = 1, $checked = null, $options = []) { }
return \Firefly\Form\Form::ffCheckbox($name, $value, $checked, $options); );
}); \Form::macro(
\Form::macro('ffOptionsList',function ($type, $name) { 'ffBalance', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffOptionsList($type, $name); return \Firefly\Form\Form::ffBalance($name, $value, $options);
}); }
);
\Form::macro(
'ffDate', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffDate($name, $value, $options);
}
);
\Form::macro(
'ffTags', function ($name, $value = null, array $options = []) {
return \Firefly\Form\Form::ffTags($name, $value, $options);
}
);
\Form::macro(
'ffCheckbox', function ($name, $value = 1, $checked = null, $options = []) {
return \Firefly\Form\Form::ffCheckbox($name, $value, $checked, $options);
}
);
\Form::macro(
'ffOptionsList', function ($type, $name) {
return \Firefly\Form\Form::ffOptionsList($type, $name);
}
);
/* /*

View File

@ -4,98 +4,53 @@
{{Form::hidden('what',$what)}} {{Form::hidden('what',$what)}}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4> <div class="panel panel-primary">
<div class="panel-heading">
<i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields
</div>
<div class="panel-body">
{{Form::ffText('name')}}
<div class="form-group">
{{ Form::label('name', 'Account name', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }}
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
@if($what == 'asset')
<span class="help-block">
Use something descriptive such as "checking account" or "My Bank Main Account".
</span>
@endif
@if($what == 'expense')
<span class="help-block">
Use something descriptive such as "Albert Heijn" or "Amazon".
</span>
@endif
@if($what == 'revenue')
<span class="help-block">
Use something descriptive such as "my mom" or "my job".
</span>
@endif
@endif
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Store new {{{$what}}} account
</button>
</p>
</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($what == 'asset')
<h4>Optional fields</h4>
<div class="form-group">
{{ Form::label('openingbalance', 'Opening balance', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">&euro;</span>
{{Form::input('number','openingbalance', Input::old('openingbalance'), ['step' => 'any', 'class' => 'form-control'])}}
</div>
@if($errors->has('openingbalance')) <div class="panel panel-default">
<p class="text-danger">{{$errors->first('openingbalance')}}</p> <div class="panel-heading">
@else <i class="fa fa-smile-o"></i> Optional fields
<span class="help-block">What's the current balance of this new account?</span> </div>
@endif <div class="panel-body">
@if($what == 'asset')
{{Form::ffBalance('openingbalance')}}
{{Form::ffDate('openingbalancedate', date('Y-m-d'))}}
@endif
{{Form::ffCheckbox('active','1',true)}}
</div> </div>
</div> </div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8"> <!-- panel for options -->
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: date('Y-m-d'), ['class' <div class="panel panel-default">
=> 'form-control']) }} <div class="panel-heading">
@if($errors->has('openingbalancedate')) <i class="fa fa-bolt"></i> Options
<p class="text-danger">{{$errors->first('openingbalancedate')}}</p> </div>
@else <div class="panel-body">
<span class="help-block">When was this the balance of the new account? Since your bank statements may lag behind, update this date to match the date of the last known balance of the account.</span> {{Form::ffOptionsList('create','account')}}
@endif
</div> </div>
</div> </div>
@endif
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-6">
<!-- add another after this one? -->
<div class="form-group">
<label for="create" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox('create',1,Input::old('create') == '1')}}
Create another (return to this form)
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Create the {{{$what}}} account</button>
</div>
</div>
</div>
</div>
{{Form::close()}} {{Form::close()}}
@stop @stop

View File

@ -1,44 +1,38 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<p class="lead">
Remember that deleting something is permanent.
</p>
</div>
</div>
{{Form::open(['class' => 'form-horizontal','url' => route('accounts.destroy',$account->id)])}} {{Form::open(['class' => 'form-horizontal','url' => route('accounts.destroy',$account->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
@if($account->transactions()->count() > 0) <div class="panel panel-red">
<p class="text-info"> <div class="panel-heading">
Account "{{{$account->name}}}" still has {{$account->transactions()->count()}} transaction(s) associated to it. Delete account "{{{$account->name}}}"
These will be deleted as well. </div>
</p> <div class="panel-body">
@endif <p>
Are you sure?
</p>
<p class="text-danger"> @if($account->transactions()->count() > 0)
Press "Delete permanently" If you are sure you want to delete "{{{$account->name}}}". <p class="text-info">
</p> 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>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="form-group"> <div class="form-group">
<div class="col-sm-8"> <div class="col-sm-8">
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
@if($account->accountType->type == 'Asset account' || $account->accountType->type == 'Default account')
<a href="{{route('accounts.asset')}}" class="btn-default btn">Cancel</a >
@endif
@if($account->accountType->type == 'Expense account' || $account->accountType->type == 'Beneficiary account')
<a href="{{route('accounts.expense')}}" class="btn-default btn">Cancel</a >
@endif
@if($account->accountType->type == 'Revenue account')
<a href="{{route('accounts.revenue')}}" class="btn-default btn">Cancel</a >
@endif
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,79 +11,46 @@
{{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}} {{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update',$account->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4> <div class="panel panel-primary">
<div class="panel-heading">
<div class="form-group"> <i class="fa {{{$subTitleIcon}}}"></i> Mandatory fields
{{ Form::label('name', 'Account name', ['class' => 'col-sm-4 control-label'])}} </div>
<div class="col-sm-8"> <div class="panel-body">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }} {{Form::ffText('name')}}
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<span
class="help-block">Use something descriptive such as "checking account" or "Albert Heijn".</span>
@endif
</div> </div>
</div> </div>
<p>
<button type="submit" class="btn btn-lg btn-success">
Update account
</button>
</p>
</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->type == 'Default account' || $account->accounttype->type == 'Asset account') <div class="panel panel-default">
<h4>Optional fields</h4> <div class="panel-heading">
<i class="fa fa-smile-o"></i> Optional fields
<div class="form-group"> </div>
{{ Form::label('openingbalance', 'Opening balance', ['class' => 'col-sm-4 control-label'])}} <div class="panel-body">
<div class="col-sm-8"> {{Form::ffCheckbox('active','1')}}
<div class="input-group"> @if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account')
<span class="input-group-addon">&euro;</span> {{Form::ffBalance('openingbalance')}}
@if(!is_null($openingBalance)) {{Form::ffDate('openingbalancedate')}}
{{Form::input('number','openingbalance', Input::old('openingbalance') ?: $openingBalance->transactions[1]->amount, ['step' => 'any', 'class' => 'form-control'])}}
@else
{{Form::input('number','openingbalance', Input::old('openingbalance'), ['step' => 'any', 'class' => 'form-control'])}}
@endif
</div>
@if($errors->has('openingbalance'))
<p class="text-danger">{{$errors->first('openingbalance')}}</p>
@else
<span class="help-block">What's the current balance of this new account?</span>
@endif @endif
</div> </div>
</div> </div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}} <!-- panel for options -->
<div class="col-sm-8"> <div class="panel panel-default">
@if(!is_null($openingBalance)) <div class="panel-heading">
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: $openingBalance->date->format('Y-m-d'), ['class' => 'form-control']) }} <i class="fa fa-bolt"></i> Options
@else </div>
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: '', ['class' => 'form-control']) }} <div class="panel-body">
@endif {{Form::ffOptionsList('create','account')}}
@if($errors->has('openingbalancedate'))
<p class="text-danger">{{$errors->first('openingbalancedate')}}</p>
@else
<span class="help-block">When was this the balance of the new account? Since your bank statements may lag behind, update this date to match the date of the last known balance of the account.</span>
@endif
</div> </div>
</div> </div>
@endif
</div> </div>
</div> </div>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-default btn-success">Update {{{$account->name}}}</button>
</div>
</div>
</div>
</div>
{{Form::close()}} {{Form::close()}}
@stop @stop

View File

@ -2,42 +2,49 @@
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly <div class="panel panel-default">
<small>Accounts</small> <div class="panel-heading">
</h1> <i class="fa {{{$subTitleIcon}}}"></i> {{{$subTitle}}}
<p class="lead">
Accounts are the record holders for transactions and transfers. Money moves from one account to another. <!-- ACTIONS MENU -->
</p> <div class="pull-right">
<p class="text-info"> <div class="btn-group">
In a double-entry bookkeeping system almost <em>everything</em> is an account. Your own personal <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
bank accounts are representated as accounts (naturally), but the stores you buy stuff at are also Actions
represented as accounts. Likewise, if you have a job, your salary is drawn from their account. <span class="caret"></span>
</p> </button>
<p> <ul class="dropdown-menu pull-right" role="menu">
<a href="{{route('accounts.create')}}" class="btn btn-success">Create a new account</a> <li><a href="{{route('accounts.create',$what)}}"><i class="fa fa-plus fa-fw"></i> New {{$what}} account</a></li>
</p> </ul>
</div>
</div>
</div>
<div class="panel-body">
<table id="accountTable" class="table table-striped table-bordered" >
<thead>
<tr>
<th>Name</th>
<th>balance</th>
<th>ID</th>
</tr>
</thead>
</table>
</div>
</div>
</div> </div>
</div> </div>
<div class="row"> @stop
<div class="col-lg-12 col-md-12 col-sm-12"> @section('scripts')
@if(count($accounts['personal']) > 0) {{HTML::script('assets/javascript/datatables/jquery.dataTables.min.js')}}
<h3>Your accounts</h3> {{HTML::script('assets/javascript/datatables/dataTables.bootstrap.js')}}
<p style="width:50%;" class="text-info"> <script type="text/javascript">
These are your personal accounts. var URL = '{{route('accounts.json',e($what))}}';
</p> </script>
<script src="assets/javascript/firefly/accounts.js"></script>
@stop
@include('accounts.list',['accounts' => $accounts['personal']]) @section('styles')
@endif {{HTML::style('assets/stylesheets/datatables/dataTables.bootstrap.css')}}
@endsection
@if(count($accounts['beneficiaries']) > 0)
<h3>Beneficiaries</h3>
<p style="width:50%;" class="text-info">
These are beneficiaries; places where you spend money or people who pay you.
</p>
@include('accounts.list',['accounts' => $accounts['beneficiaries']])
@endif
</div>
</div>
@stop

View File

@ -0,0 +1,44 @@
@extends('layouts.default')
@section('content')
<div id="sankey_multiple" style="width: 900px; height: 400px;"></div>
</body>
</html>
@stop
@section('scripts')
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1.1','packages':['sankey']}]}">
</script>
<script type="text/javascript">
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'From');
data.addColumn('string', 'To');
data.addColumn('number', 'Weight');
data.addRows([
<?php $c = 0;?>
@foreach($filtered as $index => $entry)
[ '{{{$entry['from']}}}', '{{{$entry['to']}}}', {{{$entry['amount']}}} ], // {{$c}}
<?php $c++ ?>
@endforeach
]);
// Set chart options
var options = {
sankey: {
link: { color: { fill: '#9fa8da', fillOpacity: 0.8 } },
node: { color: { fill: '#000' },
label: { color: '#000' } }
}
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.Sankey(document.getElementById('sankey_multiple'));
chart.draw(data, options);
}
</script>
@stop

View File

@ -4,68 +4,112 @@
<div class="col-lg-8 col-md-6 col-sm-12"> <div class="col-lg-8 col-md-6 col-sm-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<i class="fa {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}} <i class="fa fa-fw {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div id="chart"></div> <div id="overviewChart"></div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4 col-md-6 col-sm-12"> <div class="col-lg-4 col-md-6 col-sm-12">
<!-- time based navigation --> <!-- time based navigation -->
@include('partials.date_nav') @include('partials.date_nav')
<!-- summary of the selected period -->
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw fa-align-justify"></i> Summary
</div>
<div class="panel-body">
On the todo list.
{{--
<table class="table table-striped table-condensed">
<tr>
<th></th>
<th>Expense / income</th>
<th>Transfers</th>
</tr>
<tr>
<td>Out</td>
<td>
{{mf($show['statistics']['period']['out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=expenses"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=out"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>In</td>
<td>
{{mf($show['statistics']['period']['in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=income"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=in"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>Difference</td>
<td>{{mf($show['statistics']['period']['diff'])}}</td>
<td>{{mf($show['statistics']['period']['t_diff'])}}</td>
</tr>
</table>
--}}
</div>
</div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
Summary Out
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table table-striped table-condensed"> <div id="accountOutSankey"><img src="http://placehold.it/550x300" title="Placeholder" alt="" /></div>
<tr>
<th></th>
<th>Expense / income</th>
<th>Transfers</th>
</tr>
<tr>
<td>Out</td>
<td>
{{mf($show['statistics']['period']['out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=expenses"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_out'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=out"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>In</td>
<td>
{{mf($show['statistics']['period']['in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transactions&amp;show=income"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
<td>
{{mf($show['statistics']['period']['t_in'])}}
<a href="{{route('accounts.show',$account->id)}}?type=transfers&amp;show=in"><span class="glyphicon glyphicon-circle-arrow-right"></span></a>
</td>
</tr>
<tr>
<td>Difference</td>
<td>{{mf($show['statistics']['period']['diff'])}}</td>
<td>{{mf($show['statistics']['period']['t_diff'])}}</td>
</tr>
</table>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
Related In
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div id="accountInSankey"><img src="http://placehold.it/550x300" title="Placeholder" alt="" /></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-repeat fa-fw"></i> Transactions
</div>
<div class="panel-body">
<table id="transactionByAccountTable" class="table table-striped table-bordered" >
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Amount (&euro;)</th>
<th>From</th>
<th>To</th>
<th>Budget / category</th>
<th>ID</th>
</tr>
</thead>
</table>
{{--
<table class="table table-striped table-condensed"> <table class="table table-striped table-condensed">
@if(count($show['statistics']['accounts']) > 0) @if(count($show['statistics']['accounts']) > 0)
<tr> <tr>
@ -98,30 +142,35 @@
</tr> </tr>
@endif @endif
</table> </table>
--}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{--
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<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'],'sum' => true]) @include('paginated.transactions',['journals' => $show['journals'],'sum' => true])
</div> </div>
</div> </div>
--}}
@stop @stop
@section('styles') @section('styles')
{{HTML::style('assets/stylesheets/highslide/highslide.css')}} {{HTML::style('assets/stylesheets/highslide/highslide.css')}}
{{HTML::style('assets/stylesheets/datatables/dataTables.bootstrap.css')}}
@stop @stop
@section('scripts') @section('scripts')
<script type="text/javascript"> <script type="text/javascript">
var accountID = {{$account->id}}; var accountID = {{{$account->id}}};
</script> </script>
{{HTML::script('assets/javascript/datatables/jquery.dataTables.min.js')}}
{{HTML::script('assets/javascript/datatables/dataTables.bootstrap.js')}}
{{HTML::script('assets/javascript/highcharts/highcharts.js')}} {{HTML::script('assets/javascript/highcharts/highcharts.js')}}
{{HTML::script('assets/javascript/firefly/accounts.js')}} {{HTML::script('assets/javascript/firefly/accounts.js')}}
@stop @stop

View File

@ -98,13 +98,13 @@
<a href="#"><i class="fa fa-credit-card fa-fw"></i> Accounts <span class="fa arrow"></span></a> <a href="#"><i class="fa fa-credit-card fa-fw"></i> Accounts <span class="fa arrow"></span></a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li> <li>
<a @if($r == 'accounts.asset') class="active" @endif href="{{route('accounts.asset')}}"><i class="fa fa-money fa-fw"></i> Asset accounts</a> <a @if(isset($what) && $what == 'asset')class="active"@endif href="{{route('accounts.index','asset')}}"><i class="fa fa-money fa-fw"></i> Asset accounts</a>
</li> </li>
<li> <li>
<a @if($r == 'accounts.expense') class="active" @endif href="{{route('accounts.expense')}}"><i class="fa fa-shopping-cart fa-fw"></i> Expense accounts</a> <a @if(isset($what) && $what == 'expense')class="active"@endif href="{{route('accounts.index','expense')}}"><i class="fa fa-shopping-cart fa-fw"></i> Expense accounts</a>
</li> </li>
<li> <li>
<a @if($r == 'accounts.revenue') class="active" @endif href="{{route('accounts.revenue')}}"><i class="fa fa-download fa-fw"></i> Revenue accounts</a> <a @if(isset($what) && $what == 'revenue')class="active"@endif href="{{route('accounts.index','revenue')}}"><i class="fa fa-download fa-fw"></i> Revenue accounts</a>
</li> </li>
</ul> </ul>
<!-- /.nav-second-level --> <!-- /.nav-second-level -->
@ -208,7 +208,7 @@
</li> </li>
<!-- <!--
<li> <li>
<a href="{{route('accounts.create')}}"><i class="fa fa-money fa-fw"></i> Account</a> <a href="#"><i class="fa fa-money fa-fw"></i> Account</a>
</li> </li>
--> -->
<li> <li>

View File

@ -21,7 +21,7 @@
<p> <p>
<button type="submit" class="btn btn-lg btn-success"> <button type="submit" class="btn btn-lg btn-success">
<i class="fa fa-plus-circle"></i> Update recurring transasction <i class="fa fa-plus-circle"></i> Update recurring transaction
</button> </button>
</p> </p>
</div> </div>

View File

@ -1,12 +1,70 @@
$(function () { $(function () {
if($('#chart').length == 1) { if ($('#accountTable').length == 1) {
/** drawDatatable();
* get data from controller for home charts: }
*/ if ($('#overviewChart').length == 1) {
drawOverviewChart();
}
});
function drawDatatable() {
var opt = {
serverSide: true,
ajax: URL,
paging: true,
processing: true,
columns: [
{
name: 'name',
data: 'name',
searchable: true,
render: function (data) {
return '<a href="' + data.url + '">' + data.name + '</a>';
}
},
{
name: 'balance',
data: 'balance',
title: 'Amount (\u20AC)',
searchable: false,
sortable: true,
render: function (data) {
var amount = parseInt(data);
if (amount < 0) {
'<span class="text-danger">\u20AC ' + data.toFixed(2) + '</span>'
}
if (amount > 0) {
'<span class="text-info">\u20AC ' + data.toFixed(2) + '</span>'
}
return '<span class="text-info">\u20AC ' + data.toFixed(2) + '</span>'
}
},
{
name: 'id',
data: 'id',
title: '',
render: function (data) {
return '<div class="btn-group btn-group-xs">' +
'<a class="btn btn-default btn-xs" href="' + data.edit + '">' +
'<span class="glyphicon glyphicon-pencil"</a>' +
'<a class="btn btn-danger btn-xs" href="' + data.delete + '">' +
'<span class="glyphicon glyphicon-trash"</a>' +
'</a></div>';
}
}
]
};
$('#accountTable').DataTable(opt);
}
function drawOverviewChart() {
$.getJSON('chart/home/account/' + accountID).success(function (data) { $.getJSON('chart/home/account/' + accountID).success(function (data) {
var options = { var options = {
chart: { chart: {
renderTo: 'chart', renderTo: 'overviewChart',
type: 'spline' type: 'spline'
}, },
@ -18,7 +76,7 @@ if($('#chart').length == 1) {
allowDecimals: false, allowDecimals: false,
labels: { labels: {
formatter: function () { formatter: function () {
if(this.value >= 1000 || this.value <= -1000) { if (this.value >= 1000 || this.value <= -1000) {
return '\u20AC ' + (this.value / 1000) + 'k'; return '\u20AC ' + (this.value / 1000) + 'k';
} }
return '\u20AC ' + this.value; return '\u20AC ' + this.value;
@ -37,10 +95,10 @@ if($('#chart').length == 1) {
text: null text: null
} }
}, },
legend: {enabled:false}, legend: {enabled: false},
tooltip: { tooltip: {
formatter: function () { formatter: function () {
return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y,2); return this.series.name + ': \u20AC ' + Highcharts.numberFormat(this.y, 2);
} }
}, },
plotOptions: { plotOptions: {
@ -68,10 +126,6 @@ if($('#chart').length == 1) {
enabled: false enabled: false
} }
}; };
$('#chart').highcharts(options); $('#overviewChart').highcharts(options);
}); });
} }
});