Some more work done. Mainly accounts. [skip ci]

This commit is contained in:
James Cole
2014-07-26 08:05:02 +02:00
parent 30d5b88769
commit d088b2c558
25 changed files with 932 additions and 282 deletions

View File

@@ -8,12 +8,14 @@ use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
class AccountController extends \BaseController class AccountController extends \BaseController
{ {
protected $_accounts;
/** /**
* @param ARI $accounts * @param ARI $accounts
*/ */
public function __construct(ARI $accounts) public function __construct(ARI $accounts)
{ {
$this->accounts = $accounts; $this->_accounts = $accounts;
View::share('menu', 'accounts'); View::share('menu', 'accounts');
} }
@@ -25,7 +27,7 @@ class AccountController extends \BaseController
*/ */
public function index() public function index()
{ {
$all = $this->accounts->get(); $all = $this->_accounts->get();
$list = [ $list = [
@@ -68,23 +70,38 @@ class AccountController extends \BaseController
{ {
return View::make('accounts.create'); return View::make('accounts.create');
} }
//
// public function store()
// /** {
// * Store a newly created resource in storage.
// * $account = $this->_accounts->store(Input::all());
// * @return Response
// */ if (!$account->id) {
// public function store() // did not save, return with error:
// { Session::flash('error', 'Could not save the new account. Please check the form.');
// $account = $this->accounts->store(); return View::make('accounts.create')->withErrors($account->errors());
// if($account === false) { } else {
// Session::flash('error','Could not create account with provided information'); // saved! return to wherever.
// return Redirect::route('accounts.create')->withInput()->withErrors($this->accounts->validator); Session::flash('success', 'Account "' . $account->name . '" created!');
// } if (Input::get('create') == '1') {
// } return Redirect::route('accounts.create')->withInput();
// } else {
// return Redirect::route('accounts.index');
}
}
}
public function edit($accountId)
{
$account = $this->_accounts->find($accountId);
if ($account) {
// find the initial balance transaction, if any:
$openingBalance = $this->_accounts->findOpeningBalanceTransaction($account);
return View::make('accounts.edit')->with('account', $account)->with('openingBalance',$openingBalance);
}
}
/** /**
* Display the specified resource. * Display the specified resource.
* *
@@ -94,44 +111,44 @@ class AccountController extends \BaseController
*/ */
public function show($accountId) public function show($accountId)
{ {
return $accountId; $account = $this->_accounts->find($accountId);
return View::make('accounts.show')->with('account',$account);
} }
//
// /**
// /** * Update the specified resource in storage.
// * Show the form for editing the specified resource. *
// * * @return Response
// * @param int $id */
// * @return Response public function update()
// */ {
// public function edit($id) $account = $this->_accounts->update(Input::all());
// { Session::flash('success','Account "'.$account->name.'" updated.');
// // return Redirect::route('accounts.index');
// } }
//
// public function delete($accountId) {
// /** $account = $this->_accounts->find($accountId);
// * Update the specified resource in storage. if($account) {
// * return View::make('accounts.delete')->with('account',$account);
// * @param int $id }
// * @return Response }
// */
// public function update($id) /**
// { * @param $accountId
// // */
// } public function destroy()
// {
// $result = $this->_accounts->destroy(Input::get('id'));
// /** if($result === true) {
// * Remove the specified resource from storage. Session::flash('success','The account was deleted.');
// * return Redirect::route('accounts.index');
// * @param int $id } else {
// * @return Response Session::flash('danger','Could not delete the account. Check the logs to be sure.');
// */ return Redirect::route('accounts.index');
// public function destroy($id) }
// {
// // }
// }
} }

View File

@@ -208,8 +208,11 @@ class ChartController extends BaseController
foreach ($budget->limits as $limit) { foreach ($budget->limits as $limit) {
foreach ($limit->limitrepetitions as $rep) { foreach ($limit->limitrepetitions as $rep) {
//0: envelope for period: //0: envelope for period:
$data['series'][0]['data'][] = floatval($rep->amount); $amount = floatval($rep->amount);
$data['series'][1]['data'][] = $rep->spent; $spent = $rep->spent;
$color = $spent > $amount ? '#FF0000' : null;
$data['series'][0]['data'][] = $amount;
$data['series'][1]['data'][] = ['y' => $rep->spent, 'color' => $color];
} }
} }

View File

@@ -112,7 +112,7 @@ class TransactionController extends BaseController
$journal->categories()->save($category); $journal->categories()->save($category);
} }
Session::flash('success', 'Transaction saved'); Session::flash('success', 'Transaction "' . $description . '" saved');
if (Input::get('create') == '1') { if (Input::get('create') == '1') {
return Redirect::route('transactions.create', $what)->withInput(); return Redirect::route('transactions.create', $what)->withInput();
@@ -153,12 +153,127 @@ class TransactionController extends BaseController
*/ */
public function edit($journalId) public function edit($journalId)
{ {
// get journal:
$journal = $this->_journal->find($journalId); $journal = $this->_journal->find($journalId);
if ($journal) { if ($journal) {
// type is useful for display:
$what = strtolower($journal->transactiontype->type);
// some lists prefilled:
$budgets = $this->_budgets->getAsSelectList();
$budgets[0] = '(no budget)';
$accounts = $this->_accounts->getActiveDefaultAsSelectList(); $accounts = $this->_accounts->getActiveDefaultAsSelectList();
return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts);
// data to properly display form:
$data = [
'date' => $journal->date->format('Y-m-d'),
'category' => '',
'budget_id' => 0
];
$category = $journal->categories()->first();
if (!is_null($category)) {
$data['category'] = $category->name;
}
switch ($journal->transactiontype->type) {
case 'Withdrawal':
$data['account_id'] = $journal->transactions[0]->account->id;
$data['beneficiary'] = $journal->transactions[1]->account->name;
$data['amount'] = floatval($journal->transactions[1]->amount);
$budget = $journal->budgets()->first();
if (!is_null($budget)) {
$data['budget_id'] = $budget->id;
}
break;
case 'Deposit':
$data['account_id'] = $journal->transactions[1]->account->id;
$data['beneficiary'] = $journal->transactions[0]->account->name;
$data['amount'] = floatval($journal->transactions[1]->amount);
break;
case 'Transfer':
$data['account_from_id'] = $journal->transactions[1]->account->id;
$data['account_to_id'] = $journal->transactions[0]->account->id;
$data['amount'] = floatval($journal->transactions[1]->amount);
break;
}
return View::make('transactions.edit')->with('journal', $journal)->with('accounts', $accounts)->with(
'what', $what
)->with('budgets', $budgets)->with('data', $data);
} }
return View::make('error')->with('message', 'Invalid journal'); return View::make('error')->with('message', 'Invalid journal');
} }
public function update($journalId)
{
// get journal:
$journal = $this->_journal->find($journalId);
if ($journal) {
// update basics first:
$journal->description = Input::get('description');
$journal->date = Input::get('date');
$amount = floatval(Input::get('amount'));
// remove previous category, if any:
if (!is_null($journal->categories()->first())) {
$journal->categories()->detach($journal->categories()->first()->id);
}
// remove previous budget, if any:
if (!is_null($journal->budgets()->first())) {
$journal->budgets()->detach($journal->budgets()->first()->id);
}
// get the category:
$category = $this->_categories->findByName(Input::get('category'));
if (!is_null($category)) {
$journal->categories()->attach($category);
}
// update the amounts:
$journal->transactions[0]->amount = $amount * -1;
$journal->transactions[1]->amount = $amount;
// switch on type to properly change things:
switch ($journal->transactiontype->type) {
case 'Withdrawal':
// means transaction[0] is the users account.
$account = $this->_accounts->find(Input::get('account_id'));
$beneficiary = $this->_accounts->findByName(Input::get('beneficiary'));
$journal->transactions[0]->account()->associate($account);
$journal->transactions[1]->account()->associate($beneficiary);
// do budget:
$budget = $this->_budgets->find(Input::get('budget_id'));
$journal->budgets()->attach($budget);
break;
case 'Deposit':
// means transaction[0] is the beneficiary.
$account = $this->_accounts->find(Input::get('account_id'));
$beneficiary = $this->_accounts->findByName(Input::get('beneficiary'));
$journal->transactions[0]->account()->associate($beneficiary);
$journal->transactions[1]->account()->associate($account);
break;
case 'Transfer':
// means transaction[0] is account that sent the money (from).
$fromAccount = $this->_accounts->find(Input::get('account_from_id'));
$toAccount = $this->_accounts->find(Input::get('account_to_id'));
$journal->transactions[0]->account()->associate($fromAccount);
$journal->transactions[1]->account()->associate($toAccount);
break;
default:
throw new \Firefly\Exception\FireflyException('Cannot edit this!');
break;
}
$journal->transactions[0]->save();
$journal->transactions[1]->save();
$journal->save();
return Redirect::route('transactions.edit', $journal->id);
}
}
} }

View File

@@ -0,0 +1,41 @@
<?php
namespace Firefly\Helper\Form;
/**
* Class FormHelper
*
* @package Firefly\Form
*/
class FormHelper
{
public function budget($value = null)
{
$str = '<select name="budget_id" class="form-control">';
$str .= '<option value="0" label="(no budget)"';
if (is_null($value) || intval($value) == 0) {
$str .= ' selected="selected"';
}
$str .= '</option>';
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgets */
$budgets = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
$list = $budgets->getAsSelectList();
foreach ($list as $id => $name) {
$str .= '<option value="' . e($id) . '" label="' . e($name) . '"';
if ($id == intval($value)) {
$str .= ' selected="selected"';
}
$str .= '>' . e($name) . '</option>';
}
$str .= '</select>';
return $str;
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 25/07/14
* Time: 21:04
*/
namespace Firefly\Helper\Form;
use Illuminate\Events\Dispatcher;
/**
* Class FormTrigger
*
* @package Firefly\Helper\Form
*/
class FormTrigger {
public function registerFormExtensions() {
\Form::macro(
'budget', function () {
$helper = new \Firefly\Helper\Form\FormHelper;
return $helper->budget();
}
);
}
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen('laravel.booted', 'Firefly\Helper\Form\FormTrigger@registerFormExtensions');
}
}

View File

@@ -59,6 +59,13 @@ interface AccountRepositoryInterface
*/ */
public function getDefault(); public function getDefault();
/**
* @param \Account $account
*
* @return mixed
*/
public function findOpeningBalanceTransaction(\Account $account);
/** /**
* @return mixed * @return mixed
*/ */
@@ -72,18 +79,25 @@ interface AccountRepositoryInterface
/** /**
* @param $data * @param $data
* *
* @return mixed * @return \Account
*/ */
public function store($data); public function store($data);
/** /**
* @param $data * @param $accountId
* @param Carbon $date
* @param int $amount
* *
* @return mixed * @return bool
*/ */
public function storeWithInitialBalance($data, Carbon $date, $amount = 0); public function destroy($accountId);
/**
* @param $data
*
* @return \Account
*/
public function update($data);
/** /**
* @param $name * @param $name

View File

@@ -4,8 +4,6 @@
namespace Firefly\Storage\Account; namespace Firefly\Storage\Account;
use Carbon\Carbon; use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Database\QueryException;
/** /**
* Class EloquentAccountRepository * Class EloquentAccountRepository
@@ -14,8 +12,6 @@ use Illuminate\Database\QueryException;
*/ */
class EloquentAccountRepository implements AccountRepositoryInterface class EloquentAccountRepository implements AccountRepositoryInterface
{ {
public $validator;
/** /**
* *
*/ */
@@ -45,16 +41,6 @@ class EloquentAccountRepository implements AccountRepositoryInterface
return $list; return $list;
} }
/**
* @param $accountId
*
* @return mixed
*/
public function find($accountId)
{
return \Auth::user()->accounts()->where('id', $accountId)->first();
}
/** /**
* @param $ids * @param $ids
* *
@@ -118,73 +104,6 @@ class EloquentAccountRepository implements AccountRepositoryInterface
} }
/**
* @param $data
* @param Carbon $date
* @param int $amount
*
* @return \Account|mixed
* @throws \Firefly\Exception\FireflyException
*/
public function storeWithInitialBalance($data, Carbon $date, $amount = 0)
{
$account = $this->store($data);
$initialBalanceAT = \AccountType::where('description', 'Initial balance account')->first();
$initial = new \Account;
$initial->accountType()->associate($initialBalanceAT);
$initial->user()->associate(\Auth::user());
$initial->name = $data['name'] . ' initial balance';
$initial->active = 0;
try {
$initial->save();
} catch (QueryException $e) {
\Log::error('DB ERROR: ' . $e->getMessage());
throw new FireflyException('Could not save counterbalance account for ' . $data['name']);
}
// create new transaction journal (and transactions):
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */
$transactionJournal = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
$transactionJournal->createSimpleJournal(
$initial, $account, 'Initial Balance for ' . $data['name'], $amount, $date
);
return $account;
}
/**
* @param $data
*
* @return \Account|mixed
* @throws \Firefly\Exception\FireflyException
*/
public function store($data)
{
$defaultAT = \AccountType::where('description', 'Default account')->first();
$at = isset($data['account_type']) ? $data['account_type'] : $defaultAT;
$account = new \Account;
$account->accountType()->associate($at);
$account->user()->associate(\Auth::user());
$account->name = $data['name'];
$account->active = isset($data['active']) ? $data['active'] : 1;
try {
$account->save();
} catch (QueryException $e) {
\Log::error('DB ERROR: ' . $e->getMessage());
throw new FireflyException('Could not save account ' . $data['name']);
}
return $account;
}
/** /**
* @param $name * @param $name
* *
@@ -210,7 +129,7 @@ class EloquentAccountRepository implements AccountRepositoryInterface
$beneficiary = $this->findByName($name); $beneficiary = $this->findByName($name);
if (!$beneficiary) { if (!$beneficiary) {
$data = [ $data = [
'name' => $name, 'name' => $name,
'account_type' => $type 'account_type' => $type
]; ];
return $this->store($data); return $this->store($data);
@@ -228,6 +147,142 @@ class EloquentAccountRepository implements AccountRepositoryInterface
return \Auth::user()->accounts()->where('name', 'like', '%' . $name . '%')->first(); return \Auth::user()->accounts()->where('name', 'like', '%' . $name . '%')->first();
} }
/**
* @param $data
*
* @return \Account
* @throws \Firefly\Exception\FireflyException
*/
public function store($data)
{
$defaultAccountType = \AccountType::where('description', 'Default account')->first();
$accountType = isset($data['account_type']) ? $data['account_type'] : $defaultAccountType;
// create Account:
$account = new \Account;
$account->accountType()->associate($accountType);
$account->user()->associate(\Auth::user());
$account->name = $data['name'];
$account->active
= isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval(
$data['active']
) : 1;
// try to save it:
if ($account->save()) {
// create initial balance, if necessary:
if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) {
$amount = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
$this->_createInitialBalance($account, $amount, $date);
}
}
// whatever the result, return the account.
return $account;
}
/**
* @param \Account $account
* @param int $amount
* @param Carbon $date
*
* @return bool
*/
protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date)
{
// get account type:
$initialBalanceAT = \AccountType::where('description', 'Initial balance account')->first();
// create new account:
$initial = new \Account;
$initial->accountType()->associate($initialBalanceAT);
$initial->user()->associate(\Auth::user());
$initial->name = $account->name . ' initial balance';
$initial->active = 0;
if ($initial->validate()) {
$initial->save();
// create new transaction journal (and transactions):
/** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $transactionJournal */
$transactionJournal = \App::make(
'Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'
);
$transactionJournal->createSimpleJournal(
$initial, $account, 'Initial Balance for ' . $account->name, $amount, $date
);
return true;
}
return false;
}
/**
* @param $data
*
* @return \Account|void
*/
public function update($data)
{
$account = $this->find($data['id']);
if ($account) {
// update account accordingly:
$account->name = $data['name'];
if ($account->validate()) {
$account->save();
}
// update initial balance if necessary:
if ($account->accounttype->description == 'Default account') {
$journal = $this->findOpeningBalanceTransaction($account);
$journal->date = new Carbon($data['openingbalancedate']);
$journal->transactions[0]->amount = floatval($data['openingbalance']) * -1;
$journal->transactions[1]->amount = floatval($data['openingbalance']);
$journal->transactions[0]->save();
$journal->transactions[1]->save();
$journal->save();
}
}
return $account;
}
public function destroy($accountId) {
$account = $this->find($accountId);
if($account) {
$account->delete();
return true;
}
return false;
}
/**
* @param $accountId
*
* @return mixed
*/
public function find($accountId)
{
return \Auth::user()->accounts()->where('id', $accountId)->first();
}
/**
* @param \Account $account
*
* @return mixed|void
*/
public function findOpeningBalanceTransaction(\Account $account)
{
$transactionType = \TransactionType::where('type', 'Opening balance')->first();
$journal = \TransactionJournal::
with(
['transactions' => function ($q) {
$q->orderBy('amount', 'ASC');
}]
)->where('transaction_type_id', $transactionType->id)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transactions.account_id', $account->id)->first(['transaction_journals.*']);
return $journal;
}
/** /**
* @return mixed * @return mixed
*/ */

View File

@@ -40,6 +40,9 @@ class EloquentCategoryRepository implements CategoryRepositoryInterface
*/ */
public function findByName($name) public function findByName($name)
{ {
if($name == '') {
return null;
}
return \Auth::user()->categories()->where('name', 'LIKE', '%' . $name . '%')->first(); return \Auth::user()->categories()->where('name', 'LIKE', '%' . $name . '%')->first();
} }

View File

@@ -22,7 +22,9 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
public function find($journalId) public function find($journalId)
{ {
return \Auth::user()->transactionjournals()->with( return \Auth::user()->transactionjournals()->with(
['transactions', 'transactioncurrency', 'transactiontype', 'components', 'transactions.account', ['transactions' => function ($q) {
return $q->orderBy('amount', 'ASC');
}, 'transactioncurrency', 'transactiontype', 'components', 'transactions.account',
'transactions.account.accounttype'] 'transactions.account.accounttype']
) )
->where('id', $journalId)->first(); ->where('id', $journalId)->first();

View File

@@ -27,6 +27,8 @@ Route::group(['before' => 'auth'], function () {
Route::get('/accounts', ['uses' => 'AccountController@index', 'as' => 'accounts.index']); Route::get('/accounts', ['uses' => 'AccountController@index', 'as' => 'accounts.index']);
Route::get('/accounts/create', ['uses' => 'AccountController@create', 'as' => 'accounts.create']); Route::get('/accounts/create', ['uses' => 'AccountController@create', 'as' => 'accounts.create']);
Route::get('/accounts/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']); Route::get('/accounts/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']);
Route::get('/accounts/{account}/edit', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']);
Route::get('/accounts/{account}/delete', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']);
// budget controller: // budget controller:
Route::get('/budget/create',['uses' => 'BudgetController@create', 'as' => 'budgets.create']); Route::get('/budget/create',['uses' => 'BudgetController@create', 'as' => 'budgets.create']);
@@ -71,7 +73,9 @@ Route::group(['before' => 'csrf|auth'], function () {
Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']); Route::post('/preferences', ['uses' => 'PreferencesController@postIndex']);
// account controller: // account controller:
Route::get('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']); Route::post('/accounts/store', ['uses' => 'AccountController@store', 'as' => 'accounts.store']);
Route::post('/accounts/update', ['uses' => 'AccountController@update', 'as' => 'accounts.update']);
Route::post('/accounts/destroy', ['uses' => 'AccountController@destroy', 'as' => 'accounts.destroy']);
// limit controller: // limit controller:
Route::post('/budgets/limits/store', ['uses' => 'LimitController@store', 'as' => 'budgets.limits.store']); Route::post('/budgets/limits/store', ['uses' => 'LimitController@store', 'as' => 'budgets.limits.store']);

View File

@@ -1,66 +1,109 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col-lg-8 col-md-8 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly <h1>Firefly
<small>Add a new account</small> <small>Add a new personal account</small>
</h1> </h1>
<p class="lead"> <p class="lead">
Accounts are the record holders for transactions and transfers. Money moves Accounts are the record holders for transactions and transfers. Money moves
from one account to another. from one account to another.
</p> </p>
<p>
<p class="text-info">
In a double-entry bookkeeping system (such as this one) there is a "from" account and a "to" In a double-entry bookkeeping system (such as this one) there is a "from" account and a "to"
account, even when money is created from thin air (such as interest, or when new accounts already have account, even when money is created from thin air (such as interest, or when new accounts already have
a positive balance). a positive balance).
</p> </p>
<p>
<p class="text-info"><span class="text-danger">This form creates personal accounts only.</span>
If this is your first account, it should be a checking or savings account. Enter its name and if relevant If this is your first account, it should be a checking or savings account. Enter its name and if relevant
the current balance. Check your bank statements for the last current balance you can find. the current balance. Check your bank statements for the last current balance you can find.
</p> </p>
{{Form::open(['class' => 'form-horizontal','url' => route('accounts.store')])}}
<div class="form-group">
{{ Form::label('name', 'Account name', ['class' => 'col-sm-3 control-label'])}}
<div class="col-sm-9">
{{ Form::text('name', Input::old('name'), ['class' => 'form-control']) }}
@if($errors->has('name'))
<p class="text-danger">{{$errors->first('name')}}</p>
@else
<p class="text-info">Use something descriptive such as "checking account" or "My Bank Main Account".</p>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalance', 'Opening balance', ['class' => 'col-sm-3 control-label'])}}
<div class="col-sm-9">
{{ Form::input('number','openingbalance', Input::old('openingbalance'), ['step' => 'any', 'class' => 'form-control'])}}
@if($errors->has('openingbalance'))
<p class="text-danger">{{$errors->first('openingbalance')}}</p>
@else
<p class="text-info">What's the current balance of this new account?</p>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-3 control-label'])}}
<div class="col-sm-9">
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: date('Y-m-d'), ['class' => 'form-control']) }}
@if($errors->has('openingbalancedate'))
<p class="text-danger">{{$errors->first('openingbalancedate')}}</p>
@else
<p class="text-info">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.</p>
@endif
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-default">Create my first account</button>
</div>
</div>
</form>
</div> </div>
</div> </div>
{{Form::open(['class' => 'form-horizontal','route' => 'accounts.store'])}}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4>
<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
<span
class="help-block">Use something descriptive such as "checking account" or "My Bank Main Account".</span>
@endif
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<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'))
<p class="text-danger">{{$errors->first('openingbalance')}}</p>
@else
<span class="help-block">What's the current balance of this new account?</span>
@endif
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: date('Y-m-d'), ['class'
=> 'form-control']) }}
@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>
<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 account</button>
</div>
</div>
</div>
</div>
{{Form::close()}}
@stop @stop

View File

@@ -0,0 +1,46 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Delete "{{{$account->name}}}"</small>
</h1>
<p class="lead">
Remember that deleting something is permanent.
</p>
</div>
</div>
{{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.destroy')])}}
{{Form::hidden('id',$account->id)}}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
@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 class="text-danger">
Press "Delete permanently" If you are sure you want to delete "{{{$account->name}}}".
</p>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<div class="col-sm-8">
<button type="submit" class="btn btn-default btn-danger">Delete permanently</button>
<a href="{{route('accounts.index')}}" class="btn-default btn">Cancel</a>
</div>
</div>
</div>
</div>
{{Form::close()}}
@stop

View File

@@ -0,0 +1,94 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Edit "{{{$account->name}}}"</small>
</h1>
<p class="lead">
Accounts are the record holders for transactions and transfers. Money moves
from one account to another.
</p>
</div>
</div>
{{Form::model($account, ['class' => 'form-horizontal','url' => route('accounts.update')])}}
{{Form::hidden('id',$account->id)}}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<h4>Mandatory fields</h4>
<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
<span
class="help-block">Use something descriptive such as "checking account" or "Albert Heijn".</span>
@endif
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
@if($account->accounttype->description == 'Default account')
<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>
@if(!is_null($openingBalance))
{{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
</div>
</div>
<div class="form-group">
{{ Form::label('openingbalancedate', 'Opening balance date', ['class' => 'col-sm-4 control-label'])}}
<div class="col-sm-8">
@if(!is_null($openingBalance))
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: $openingBalance->date->format('Y-m-d'), ['class' => 'form-control']) }}
@else
{{ Form::input('date','openingbalancedate', Input::old('openingbalancedate') ?: date('Y-m-d'), ['class' => 'form-control']) }}
@endif
@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>
@endif
</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()}}
@stop

View File

@@ -5,8 +5,10 @@
<h1>Firefly <h1>Firefly
<small>Accounts</small> <small>Accounts</small>
</h1> </h1>
<h2>Index</h2> <p class="lead">
<p style="width:50%;" class="text-info"> Accounts are the record holders for transactions and transfers. Money moves from one account to another.
</p>
<p class="text-info">
In a double-entry bookkeeping system almost <em>everything</em> is an account. Your own personal In a double-entry bookkeeping system almost <em>everything</em> is an account. Your own personal
bank accounts are representated as accounts (naturally), but the stores you buy stuff at are also bank accounts are representated as accounts (naturally), but the stores you buy stuff at are also
represented as accounts. Likewise, if you have a job, your salary is drawn from their account. represented as accounts. Likewise, if you have a job, your salary is drawn from their account.

View File

@@ -3,6 +3,7 @@
<th>&nbsp;</th> <th>&nbsp;</th>
<th style="width:30%;">Name</th> <th style="width:30%;">Name</th>
<th>Current balance</th> <th>Current balance</th>
<th></th>
</tr> </tr>
@foreach($accounts as $account) @foreach($accounts as $account)
<tr> <tr>
@@ -14,6 +15,12 @@
<td> <td>
<a href="{{route('accounts.show',$account->id)}}" title="Overview for account {{{$account->name}}}">{{{$account->name}}}</a></td> <a href="{{route('accounts.show',$account->id)}}" title="Overview for account {{{$account->name}}}">{{{$account->name}}}</a></td>
<td>{{mf($account->balance())}}</td> <td>{{mf($account->balance())}}</td>
<td>
<span class="btn-group-xs btn-group">
<a href="{{route('accounts.edit',$account->id)}}" title="Edit {{{$account->name}}}" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span></a>
<a href="{{route('accounts.delete',$account->id)}}" title="Edit {{{$account->name}}}" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></a>
</span>
</td>
</tr> </tr>
@endforeach @endforeach
</table> </table>

View File

@@ -0,0 +1,29 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Overview for account "{{{$account->name}}}"</small>
</h1>
</div>
</div>
@include('partials.date_nav')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div id="chart"></div>
</div>
</div>
@stop
@section('scripts')
<script type="text/javascript">
var accountID = {{$account->id}};
</script>
<script src="assets/javascript/highcharts.js"></script>
<script src="assets/javascript/highcharts-more.js"></script>
<script src="assets/javascript/highslide-full.min.js"></script>
<script src="assets/javascript/highslide.config.js"></script>
<script src="assets/javascript/accounts.js"></script>
@stop

View File

@@ -1,29 +0,0 @@
@extends('layouts.default')
@section('content')
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
<small>Budgets and limits</small>
</h1>
<p class="text-info">
These are your budgets and if set, their "limits". Firefly uses an "<a
href="http://en.wikipedia.org/wiki/Envelope_System" class="text-success">envelope system</a>" for your
budgets,
which means that for each period of time (for example a month) a virtual "envelope" can be created
containing a certain amount of money. Money spent within a budget is removed from the envelope.
</p>
<p>
@if($group == 'budget')
<a class="btn btn-default" href ="{{route('budgets.index','date')}}"><span class="glyphicon glyphicon-th-list"></span> Group by date</a>
@else
<a class="btn btn-default" href ="{{route('budgets.index','budget')}}"><span class="glyphicon glyphicon-th-list"></span> Group by budget</a>
@endif
</p>
</div>
</div>
@if($group == 'budget')
@include('budgets.index-budget')
@else
@include('budgets.index-date')
@endif
@stop

View File

@@ -1,6 +1,17 @@
@extends('layouts.default') @extends('layouts.default')
@section('content') @section('content')
@include('partials.date_nav') <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<h1>Firefly
@if($count > 0)
<small>What's playing?</small>
@endif
</h1>
</div>
</div>
@if($count > 0)
@include('partials.date_nav')
@endif
@if($count == 0) @if($count == 0)
<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">
@@ -66,9 +77,10 @@
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
<h4>Budgets</h4> <h4>Budgets</h4>
<div id="budgets"> <div id="budgets"></div>
<p>
</div> <a class="btn btn-default btn-xs" href ="{{route('budgets.limits.create')}}?startdate={{Session::get('start')->format('Y-m-d')}}"><span class="glyphicon glyphicon-plus-sign"></span> Create a limit</a>
</p>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,6 @@
<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 <form role="form" method="GET" action="{{Request::url()}}">
@if($count > 0)
<small>What's playing?</small>
@endif
</h1>
@if($count > 0)
<form role="form" method="GET">
<?php $r = Session::get('range', '1M'); ?> <?php $r = Session::get('range', '1M'); ?>
<div class="row"> <div class="row">
<div class="col-lg-2"> <div class="col-lg-2">
@@ -55,7 +49,6 @@
</form> </form>
@endif
</div> </div>
</div> </div>

View File

@@ -171,6 +171,7 @@
</div> </div>
</div> </div>
</div> </div>
{{Form::close()}}
@stop @stop
@section('scripts') @section('scripts')

View File

@@ -3,7 +3,7 @@
<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 <h1>Firefly
<small>Edit transaction ""</small> <small>Edit transaction "{{{$journal->description}}}"</small>
</h1> </h1>
</div> </div>
</div> </div>
@@ -14,61 +14,101 @@
account <em>A</em> to account <em>B</em>. account <em>A</em> to account <em>B</em>.
</p> </p>
<p class="text-info"> <p class="text-info">
A deposit is when you earn money, moving an amount from a beneficiary into your own account. Updating a transaction will also update balances, budgets and other records.
</p> </p>
</div> </div>
</div> </div>
{{Form::open(['class' => 'form-horizontal'])}} {{Form::open(['class' => 'form-horizontal','url' => route('transactions.update',$journal->id)])}}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<h4>Mandatory fields</h4> <h4>Mandatory fields</h4>
<!-- ALWAYS AVAILABLE -->
<div class="form-group"> <div class="form-group">
<label for="description" class="col-sm-4 control-label">Description</label> <label for="description" class="col-sm-4 control-label">Description</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="text" name="description" value="{{{Input::old('description')}}}" autocomplete="off" class="form-control" placeholder="Description" /> <input type="text" name="description" value="{{{Input::old('description') ?: $journal->description}}}" autocomplete="off" class="form-control" placeholder="Description" />
</div> </div>
</div> </div>
<!-- SHOW ACCOUNT (FROM) ONLY FOR WITHDRAWALS AND DEPOSITS -->
@if($what == 'deposit' || $what == 'withdrawal')
<div class="form-group"> <div class="form-group">
<label for="beneficiary" class="col-sm-4 control-label">Beneficiary (payer)</label> <label for="account_id" class="col-sm-4 control-label">
@if($what == 'deposit')
Receiving account
@endif
@if($what == 'withdrawal')
Paid from account
@endif
</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="text" name="beneficiary" value="{{{Input::old('beneficiary')}}}" autocomplete="off" class="form-control" placeholder="Beneficiary" /> {{Form::select('account_id',$accounts,Input::old('account_id') ?: $data['account_id'],['class' => 'form-control'])}}
</div>
</div>
@endif
<!-- SHOW BENEFICIARY (ACCOUNT TO) ONLY FOR WITHDRAWALS AND DEPOSITS -->
@if($what == 'deposit' || $what == 'withdrawal')
<div class="form-group">
<label for="beneficiary" class="col-sm-4 control-label">
@if($what == 'deposit')
Paying beneficiary
@endif
@if($what == 'withdrawal')
Beneficiary
@endif
</label>
<div class="col-sm-8">
<input type="text" name="beneficiary" value="{{{Input::old('beneficiary') ?: $data['beneficiary']}}}" autocomplete="off" class="form-control" placeholder="Beneficiary" />
<span class="help-block">This field will auto-complete your existing beneficiaries (if any), but you can type freely to create new ones.</span> <span class="help-block">This field will auto-complete your existing beneficiaries (if any), but you can type freely to create new ones.</span>
</div> </div>
</div> </div>
@endif
<!-- ONLY SHOW FROM/TO ACCOUNT WHEN CREATING TRANSFER -->
@if($what == 'transfer')
<div class="form-group"> <div class="form-group">
<label for="account_id" class="col-sm-4 control-label">Account</label> <label for="account_from_id" class="col-sm-4 control-label">Account from</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{Form::select('account_id',$accounts,Input::old('account_id'),['class' => 'form-control'])}} {{Form::select('account_to_id',$accounts,Input::old('account_from_id') ?: $data['account_from_id'],['class' => 'form-control'])}}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="amount" class="col-sm-4 control-label">Amount spent</label> <label for="account_to_id" class="col-sm-4 control-label">Account to</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="number" name="amount" min="0.01" value="{{Input::old('amount') or 0}}" step="any" class="form-control" /> {{Form::select('account_from_id',$accounts,Input::old('account_to_id') ?: $data['account_to_id'],['class' => 'form-control'])}}
</div>
</div>
@endif
<!-- ALWAYS SHOW AMOUNT -->
<div class="form-group">
<label for="amount" class="col-sm-4 control-label">
@if($what == 'withdrawal')
Amount spent
@endif
@if($what == 'deposit')
Amount received
@endif
@if($what == 'transfer')
Amount transferred
@endif
</label>
<div class="col-sm-8">
<input type="number" name="amount" min="0.01" value="{{Input::old('amount') ?: $data['amount']}}" step="any" class="form-control" />
</div> </div>
</div> </div>
<!-- ALWAYS SHOW DATE -->
<div class="form-group"> <div class="form-group">
<label for="date" class="col-sm-4 control-label">Date</label> <label for="date" class="col-sm-4 control-label">Date</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="date" name="date" value="{{Input::old('date') ?: date('Y-m-d')}}" class="form-control" /> <input type="date" name="date" value="{{Input::old('date') ?: $data['date']}}" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="submit" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<input type="submit" name="submit" value="Create deposit" class="btn btn-info" />
</div> </div>
</div> </div>
@@ -76,10 +116,21 @@
<div class="col-lg-6 col-md-12 col-sm-12"> <div class="col-lg-6 col-md-12 col-sm-12">
<h4>Optional fields</h4> <h4>Optional fields</h4>
<!-- BUDGET ONLY WHEN CREATING A WITHDRAWAL -->
@if($what == 'withdrawal')
<div class="form-group">
<label for="budget_id" class="col-sm-4 control-label">Budget</label>
<div class="col-sm-8">
{{Form::select('budget_id',$budgets,Input::old('budget_id') ?: $data['budget_id'],['class' => 'form-control'])}}
<span class="help-block">Select one of your budgets to make this transaction a part of it.</span>
</div>
</div>
@endif
<!-- CATEGORY ALWAYS -->
<div class="form-group"> <div class="form-group">
<label for="category" class="col-sm-4 control-label">Category</label> <label for="category" class="col-sm-4 control-label">Category</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input type="text" name="category" value="" autocomplete="off" class="form-control" placeholder="Category" /> <input type="text" name="category" value="{{Input::old('category') ?: $data['category']}}" autocomplete="off" class="form-control" placeholder="Category" />
<span class="help-block">Add more fine-grained information to this transaction by entering a category. <span class="help-block">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 Like the beneficiary-field, this field will auto-complete existing categories but can also be used
to create new ones. to create new ones.
@@ -88,7 +139,20 @@
</div> </div>
</div> </div>
</div>
<div class="row">
<div class="col-lg-6">
<!-- ALWAYS SHOW SUBMit -->
<div class="form-group">
<label for="submit" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<input type="submit" name="submit" value="Update {{$what}}" class="btn btn-info" />
</div>
</div>
</div>
</div>
{{Form::close()}}
@stop @stop

View File

@@ -49,7 +49,15 @@
<a href="#">{{{$journal->transactions[1]->account->name}}}</a> <a href="#">{{{$journal->transactions[1]->account->name}}}</a>
@endif @endif
</td> </td>
<td>Edit / delete</td> <td>
<div class="btn-group btn-group-xs">
<a href="{{route('transactions.edit',$journal->id)}}" class="btn btn-default">
<span class="glyphicon glyphicon-pencil"></span>
<a href="{{route('transactions.delete',$journal->id)}}" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</a>
</div>
</td>
</tr> </tr>
@endforeach @endforeach
</table> </table>

View File

@@ -12,6 +12,7 @@
*/ */
if (!function_exists('mf')) { if (!function_exists('mf')) {
function mf($n, $coloured = true) function mf($n, $coloured = true)
{ {
@@ -35,6 +36,8 @@ if (!function_exists('mf')) {
$app = new Illuminate\Foundation\Application; $app = new Illuminate\Foundation\Application;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Detect The Application Environment | Detect The Application Environment
@@ -83,6 +86,8 @@ $framework = $app['path.base'] .
require $framework . '/Illuminate/Foundation/start.php'; require $framework . '/Illuminate/Foundation/start.php';
Event::subscribe('Firefly\Helper\Form\FormTrigger');
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Return The Application | Return The Application
@@ -97,5 +102,11 @@ require $framework . '/Illuminate/Foundation/start.php';
// do something with events: // do something with events:
Event::subscribe('Firefly\Trigger\Limits\EloquentLimitTrigger'); Event::subscribe('Firefly\Trigger\Limits\EloquentLimitTrigger');
App::booted(function() {
$forms = new \Firefly\Helper\Form\FormTrigger;
$forms->registerFormExtensions();
}
);
return $app; return $app;

View File

@@ -0,0 +1,95 @@
$(function () {
if($('#chart').length == 1) {
/**
* get data from controller for home charts:
*/
$.getJSON('chart/home/account/' + accountID).success(function (data) {
var options = {
chart: {
renderTo: 'chart',
type: 'line'
},
series: data,
title: {
text: 'BETTER TITLE HERE'
},
yAxis: {
formatter: function () {
return '$' + Highcharts.numberFormat(this.y, 0);
}
},
subtitle: {
text: '<a href="#">View more</a>',
useHTML: true
},
xAxis: {
floor: 0,
type: 'datetime',
dateTimeLabelFormats: {
day: '%e %b',
year: '%b'
},
title: {
text: 'Date'
}
},
tooltip: {
shared: true,
crosshairs: false,
formatter: function () {
var str = '<span style="font-size:80%;">' + Highcharts.dateFormat("%A, %e %B", this.x) + '</span><br />';
for (x in this.points) {
var point = this.points[x];
var colour = point.point.pointAttr[''].fill;
str += '<span style="color:' + colour + '">' + point.series.name + '</span>: € ' + Highcharts.numberFormat(point.y, 2) + '<br />';
}
//console.log();
return str;
return '<span style="font-size:80%;">' + this.series.name + ' on ' + Highcharts.dateFormat("%e %B", this.x) + ':</span><br /> € ' + Highcharts.numberFormat(this.y, 2);
}
},
plotOptions: {
line: {
shadow: true
},
series: {
cursor: 'pointer',
negativeColor: '#FF0000',
threshold: 0,
lineWidth: 1,
marker: {
radius: 2
},
point: {
events: {
click: function (e) {
hs.htmlExpand(null, {
src: 'chart/home/info/' + this.series.name + '/' + Highcharts.dateFormat("%d/%m/%Y", this.x),
pageOrigin: {
x: e.pageX,
y: e.pageY
},
objectType: 'ajax',
headingText: '<a href="#">' + this.series.name + '</a>',
width: 250
}
)
;
}
}
}
}
},
credits: {
enabled: false
}
};
$('#chart').highcharts(options);
});
}
});

View File

@@ -197,6 +197,7 @@ $(function () {
bar: { bar: {
dataLabels: { dataLabels: {
enabled: true, enabled: true,
formatter: function() {return '€ ' + Highcharts.numberFormat(this.y,2);}
} }
} }
}, },
@@ -215,24 +216,6 @@ $(function () {
enabled: false enabled: false
}, },
series: data.series series: data.series
// [
// {
// name: 'Budget in X',
// data: [107, 31, 635, 203, 2]
// },
// {
// name: 'Expense in X',
// data: [107, 31, 635, 203, 2]
// },
// {
// name: 'Budget now',
// data: [133, 156, 947, 408, 6]
// },
// {
// name: 'Expense now',
// data: [973, 914, 454, 732, 34]
// }
// ]
}); });
}); });