mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
New charts + tests.
This commit is contained in:
parent
138044fb41
commit
5645f7a893
@ -1,17 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Carbon\Carbon as Carbon;
|
|
||||||
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
|
|
||||||
use Firefly\Helper\Toolkit\Toolkit as tk;
|
use Firefly\Helper\Toolkit\Toolkit as tk;
|
||||||
|
use Firefly\Storage\Account\AccountRepositoryInterface as ARI;
|
||||||
|
use Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface as TJRI;
|
||||||
|
|
||||||
class ChartController extends BaseController
|
class ChartController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $accounts;
|
protected $accounts;
|
||||||
|
protected $journals;
|
||||||
|
|
||||||
public function __construct(ARI $accounts)
|
public function __construct(ARI $accounts, TJRI $journals)
|
||||||
{
|
{
|
||||||
$this->accounts = $accounts;
|
$this->accounts = $accounts;
|
||||||
|
$this->journals = $journals;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +21,7 @@ class ChartController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function homeAccount($account = null)
|
public function homeAccount($account = null)
|
||||||
{
|
{
|
||||||
list($start,$end) = tk::getDateRange();
|
list($start, $end) = tk::getDateRange();
|
||||||
$current = clone $start;
|
$current = clone $start;
|
||||||
|
|
||||||
// chart
|
// chart
|
||||||
@ -27,7 +29,6 @@ class ChartController extends BaseController
|
|||||||
$chart->addColumn('Day of the month', 'date');
|
$chart->addColumn('Day of the month', 'date');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (is_null($account)) {
|
if (is_null($account)) {
|
||||||
// get accounts:
|
// get accounts:
|
||||||
$accounts = $this->accounts->getActiveDefault();
|
$accounts = $this->accounts->getActiveDefault();
|
||||||
@ -61,7 +62,68 @@ class ChartController extends BaseController
|
|||||||
|
|
||||||
$chart->generate();
|
$chart->generate();
|
||||||
return $chart->getData();
|
return $chart->getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all budgets used in transaction(journals) this period:
|
||||||
|
*/
|
||||||
|
public function homeBudgets()
|
||||||
|
{
|
||||||
|
list($start, $end) = tk::getDateRange();
|
||||||
|
|
||||||
|
$result = $this->journals->homeBudgetChart($start, $end);
|
||||||
|
|
||||||
|
// create a chart:
|
||||||
|
$chart = App::make('gchart');
|
||||||
|
$chart->addColumn('Budget', 'string');
|
||||||
|
$chart->addColumn('Amount', 'number');
|
||||||
|
foreach ($result as $name => $amount) {
|
||||||
|
$chart->addRow($name, $amount);
|
||||||
|
}
|
||||||
|
$chart->generate();
|
||||||
|
return Response::json($chart->getData());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all categories used in transaction(journals) this period.
|
||||||
|
*/
|
||||||
|
public function homeCategories()
|
||||||
|
{
|
||||||
|
list($start, $end) = tk::getDateRange();
|
||||||
|
|
||||||
|
$result = $this->journals->homeCategoryChart($start, $end);
|
||||||
|
|
||||||
|
// create a chart:
|
||||||
|
$chart = App::make('gchart');
|
||||||
|
$chart->addColumn('Category', 'string');
|
||||||
|
$chart->addColumn('Amount', 'number');
|
||||||
|
foreach ($result as $name => $amount) {
|
||||||
|
$chart->addRow($name, $amount);
|
||||||
|
}
|
||||||
|
$chart->generate();
|
||||||
|
return Response::json($chart->getData());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all beneficiaries used in transaction(journals) this period.
|
||||||
|
*/
|
||||||
|
public function homeBeneficiaries()
|
||||||
|
{
|
||||||
|
list($start, $end) = tk::getDateRange();
|
||||||
|
|
||||||
|
$result = $this->journals->homeBeneficiaryChart($start, $end);
|
||||||
|
|
||||||
|
// create a chart:
|
||||||
|
$chart = App::make('gchart');
|
||||||
|
$chart->addColumn('Beneficiary', 'string');
|
||||||
|
$chart->addColumn('Amount', 'number');
|
||||||
|
foreach ($result as $name => $amount) {
|
||||||
|
$chart->addRow($name, $amount);
|
||||||
|
}
|
||||||
|
$chart->generate();
|
||||||
|
return Response::json($chart->getData());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -135,6 +135,99 @@ class EloquentTransactionJournalRepository implements TransactionJournalReposito
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function homeBudgetChart(\Carbon\Carbon $start, \Carbon\Carbon $end)
|
||||||
|
{
|
||||||
|
return $this->homeComponentChart($start, $end, 'Budget');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function homeComponentChart(\Carbon\Carbon $start, \Carbon\Carbon $end, $chartType)
|
||||||
|
{
|
||||||
|
|
||||||
|
// lets make this simple.
|
||||||
|
$types = [];
|
||||||
|
foreach (\TransactionType::whereIn('type', ['Deposit', 'Withdrawal'])->get() as $t) {
|
||||||
|
$types[] = $t->id;
|
||||||
|
}
|
||||||
|
unset($t);
|
||||||
|
|
||||||
|
// get all journals, partly filtered:
|
||||||
|
$journals = \TransactionJournal::
|
||||||
|
with(
|
||||||
|
['components' => function ($q) use ($chartType) {
|
||||||
|
$q->where('class', $chartType);
|
||||||
|
}, 'transactions' => function ($q) {
|
||||||
|
$q->where('amount', '>', 0);
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
->after($start)->before($end)
|
||||||
|
->whereIn('transaction_type_id', $types)
|
||||||
|
->get(['transaction_journals.*']);
|
||||||
|
unset($types);
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($journals as $journal) {
|
||||||
|
// has to be one:
|
||||||
|
$transaction = $journal->transactions[0];
|
||||||
|
$amount = floatval($transaction->amount);
|
||||||
|
|
||||||
|
|
||||||
|
// MIGHT be one:
|
||||||
|
$budget = isset($journal->components[0]) ? $journal->components[0] : null;
|
||||||
|
if (!is_null($budget)) {
|
||||||
|
$name = $budget->name;
|
||||||
|
} else {
|
||||||
|
$name = '(no budget)';
|
||||||
|
}
|
||||||
|
$result[$name] = isset($result[$name]) ? $result[$name] + $amount : $amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
unset($journal, $transaction, $budget, $name, $amount);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function homeCategoryChart(\Carbon\Carbon $start, \Carbon\Carbon $end)
|
||||||
|
{
|
||||||
|
return $this->homeComponentChart($start, $end, 'Category');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function homeBeneficiaryChart(\Carbon\Carbon $start, \Carbon\Carbon $end)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
// lets make this simple.
|
||||||
|
$types = [];
|
||||||
|
foreach (\TransactionType::whereIn('type', ['Deposit', 'Withdrawal'])->get() as $t) {
|
||||||
|
$types[] = $t->id;
|
||||||
|
}
|
||||||
|
unset($t);
|
||||||
|
|
||||||
|
// account type we want to see:
|
||||||
|
$accountType = \AccountType::where('description', 'Beneficiary account')->first();
|
||||||
|
$accountTypeID = $accountType->id;
|
||||||
|
|
||||||
|
// get all journals, partly filtered:
|
||||||
|
$journals = \TransactionJournal::
|
||||||
|
with(
|
||||||
|
['transactions', 'transactions.account' => function ($q) use ($accountTypeID) {
|
||||||
|
$q->where('account_type_id', $accountTypeID);
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
->after($start)->before($end)
|
||||||
|
->whereIn('transaction_type_id', $types)
|
||||||
|
->get(['transaction_journals.*']);
|
||||||
|
foreach ($journals as $journal) {
|
||||||
|
foreach ($journal->transactions as $t) {
|
||||||
|
if (!is_null($t->account)) {
|
||||||
|
$name = $t->account->name;
|
||||||
|
$amount = floatval($t->amount) < 0 ? floatval($t->amount) * -1 : floatval($t->amount);
|
||||||
|
|
||||||
|
$result[$name] = isset($result[$name]) ? $result[$name]+$amount : $amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function getByAccount(\Account $account, $count = 25)
|
public function getByAccount(\Account $account, $count = 25)
|
||||||
{
|
{
|
||||||
$accountID = $account->id;
|
$accountID = $account->id;
|
||||||
|
@ -11,4 +11,10 @@ interface TransactionJournalRepositoryInterface
|
|||||||
|
|
||||||
public function getByAccount(\Account $account, $count = 25);
|
public function getByAccount(\Account $account, $count = 25);
|
||||||
|
|
||||||
|
public function homeBudgetChart(\Carbon\Carbon $start, \Carbon\Carbon $end);
|
||||||
|
public function homeCategoryChart(\Carbon\Carbon $start, \Carbon\Carbon $end);
|
||||||
|
public function homeBeneficiaryChart(\Carbon\Carbon $start, \Carbon\Carbon $end);
|
||||||
|
|
||||||
|
public function homeComponentChart(\Carbon\Carbon $start, \Carbon\Carbon $end, $chartType);
|
||||||
|
|
||||||
}
|
}
|
@ -48,4 +48,11 @@ class TransactionJournal extends Elegant
|
|||||||
return array('created_at', 'updated_at', 'date');
|
return array('created_at', 'updated_at', 'date');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeAfter($query, \Carbon\Carbon $date) {
|
||||||
|
return $query->where('date','>=',$date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
public function scopeBefore($query, \Carbon\Carbon $date) {
|
||||||
|
return $query->where('date','<=',$date->format('Y-m-d'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,9 @@ Route::group(['before' => 'auth'], function () {
|
|||||||
|
|
||||||
// chart controller
|
// chart controller
|
||||||
Route::get('/chart/home/account/{account?}', ['uses' => 'ChartController@homeAccount', 'as' => 'chart.home']);
|
Route::get('/chart/home/account/{account?}', ['uses' => 'ChartController@homeAccount', 'as' => 'chart.home']);
|
||||||
|
Route::get('/chart/home/budgets', ['uses' => 'ChartController@homeBudgets', 'as' => 'chart.budgets']);
|
||||||
|
Route::get('/chart/home/beneficiaries', ['uses' => 'ChartController@homeBeneficiaries', 'as' => 'chart.beneficiaries']);
|
||||||
|
Route::get('/chart/home/categories', ['uses' => 'ChartController@homeCategories', 'as' => 'chart.categories']);
|
||||||
|
|
||||||
// preferences controller
|
// preferences controller
|
||||||
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
|
Route::get('/preferences', ['uses' => 'PreferencesController@index', 'as' => 'preferences']);
|
||||||
|
@ -132,4 +132,94 @@ class ChartControllerTest extends TestCase
|
|||||||
$this->assertResponseOk();
|
$this->assertResponseOk();
|
||||||
$this->assertViewHas('message');
|
$this->assertViewHas('message');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testhomeBudgets()
|
||||||
|
{
|
||||||
|
// mock preference:
|
||||||
|
$pref = $this->mock('Preference');
|
||||||
|
$pref->shouldReceive('getAttribute', 'data')->andReturn('week');
|
||||||
|
|
||||||
|
$start = new \Carbon\Carbon;
|
||||||
|
$end = new \Carbon\Carbon;
|
||||||
|
|
||||||
|
// mock transaction journal
|
||||||
|
$tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
|
||||||
|
$tj->shouldReceive('homeBudgetChart')->andReturn(['entry' => 30]);
|
||||||
|
|
||||||
|
// mock toolkit:
|
||||||
|
$toolkit = $this->mock('Firefly\Helper\Toolkit\ToolkitInterface');
|
||||||
|
$toolkit->shouldReceive('getDateRange')->andReturn([$start, $end]);
|
||||||
|
|
||||||
|
|
||||||
|
// mock preferences helper:
|
||||||
|
$preferences = $this->mock('Firefly\Helper\Preferences\PreferencesHelperInterface');
|
||||||
|
$preferences->shouldReceive('get')->with('viewRange', 'week')->once()->andReturn($pref);
|
||||||
|
|
||||||
|
|
||||||
|
// call
|
||||||
|
$this->call('GET', '/chart/home/budgets');
|
||||||
|
|
||||||
|
// test
|
||||||
|
$this->assertResponseOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testhomeCategories()
|
||||||
|
{
|
||||||
|
// mock preference:
|
||||||
|
$pref = $this->mock('Preference');
|
||||||
|
$pref->shouldReceive('getAttribute', 'data')->andReturn('week');
|
||||||
|
|
||||||
|
$start = new \Carbon\Carbon;
|
||||||
|
$end = new \Carbon\Carbon;
|
||||||
|
|
||||||
|
// mock transaction journal
|
||||||
|
$tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
|
||||||
|
$tj->shouldReceive('homeCategoryChart')->andReturn(['entry' => 30]);
|
||||||
|
|
||||||
|
// mock toolkit:
|
||||||
|
$toolkit = $this->mock('Firefly\Helper\Toolkit\ToolkitInterface');
|
||||||
|
$toolkit->shouldReceive('getDateRange')->andReturn([$start, $end]);
|
||||||
|
|
||||||
|
|
||||||
|
// mock preferences helper:
|
||||||
|
$preferences = $this->mock('Firefly\Helper\Preferences\PreferencesHelperInterface');
|
||||||
|
$preferences->shouldReceive('get')->with('viewRange', 'week')->once()->andReturn($pref);
|
||||||
|
|
||||||
|
|
||||||
|
// call
|
||||||
|
$this->call('GET', '/chart/home/categories');
|
||||||
|
|
||||||
|
// test
|
||||||
|
$this->assertResponseOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testhomeBeneficiaries()
|
||||||
|
{
|
||||||
|
// mock preference:
|
||||||
|
$pref = $this->mock('Preference');
|
||||||
|
$pref->shouldReceive('getAttribute', 'data')->andReturn('week');
|
||||||
|
|
||||||
|
$start = new \Carbon\Carbon;
|
||||||
|
$end = new \Carbon\Carbon;
|
||||||
|
|
||||||
|
// mock transaction journal
|
||||||
|
$tj = $this->mock('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface');
|
||||||
|
$tj->shouldReceive('homeBeneficiaryChart')->andReturn(['entry' => 30]);
|
||||||
|
|
||||||
|
// mock toolkit:
|
||||||
|
$toolkit = $this->mock('Firefly\Helper\Toolkit\ToolkitInterface');
|
||||||
|
$toolkit->shouldReceive('getDateRange')->andReturn([$start, $end]);
|
||||||
|
|
||||||
|
|
||||||
|
// mock preferences helper:
|
||||||
|
$preferences = $this->mock('Firefly\Helper\Preferences\PreferencesHelperInterface');
|
||||||
|
$preferences->shouldReceive('get')->with('viewRange', 'week')->once()->andReturn($pref);
|
||||||
|
|
||||||
|
|
||||||
|
// call
|
||||||
|
$this->call('GET', '/chart/home/beneficiaries');
|
||||||
|
|
||||||
|
// test
|
||||||
|
$this->assertResponseOk();
|
||||||
|
}
|
||||||
}
|
}
|
@ -81,14 +81,18 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4 col-sm-6 col-md-6">
|
<div class="col-lg-4 col-sm-6 col-md-6">
|
||||||
<h4>Beneficiaries</h4>
|
<h4>Beneficiaries</h4>
|
||||||
|
<div id="beneficiaryChart"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-sm-6 col-md-6">
|
<div class="col-lg-4 col-sm-6 col-md-6">
|
||||||
<h4>Categories</h4>
|
<h4>Categories</h4>
|
||||||
|
<div id="categoryChart"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4 col-sm-6 col-md-6">
|
<div class="col-lg-4 col-sm-6 col-md-6">
|
||||||
<h4>Budgets</h4>
|
<h4>Budgets</h4>
|
||||||
|
<div id="budgetChart"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br /><br /><br /><br /><br />
|
||||||
|
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
function drawChart(id,URL,opt) {
|
function drawChart(id,URL,type,opt) {
|
||||||
$.getJSON(URL).success(function (data) {
|
$.getJSON(URL).success(function (data) {
|
||||||
$(id).removeClass('loading');
|
$(id).removeClass('loading');
|
||||||
|
|
||||||
@ -7,7 +7,17 @@ function drawChart(id,URL,opt) {
|
|||||||
var money = new google.visualization.NumberFormat({decimalSymbol: ',', groupingSymbol: '.', prefix: '€ '});
|
var money = new google.visualization.NumberFormat({decimalSymbol: ',', groupingSymbol: '.', prefix: '€ '});
|
||||||
money.format(gdata, 1);
|
money.format(gdata, 1);
|
||||||
var gID = id.substring(1);
|
var gID = id.substring(1);
|
||||||
var chart = new google.visualization.LineChart(document.getElementById(gID));
|
var chart;
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
case 'LineChart':
|
||||||
|
chart = new google.visualization.LineChart(document.getElementById(gID));
|
||||||
|
break;
|
||||||
|
case 'PieChart':
|
||||||
|
chart = new google.visualization.PieChart(document.getElementById(gID));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
chart.draw(gdata, opt);
|
chart.draw(gdata, opt);
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ google.setOnLoadCallback(chartCallback);
|
|||||||
|
|
||||||
function chartCallback() {
|
function chartCallback() {
|
||||||
drawAccountChart();
|
drawAccountChart();
|
||||||
|
drawExtraCharts();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawAccountChart() {
|
function drawAccountChart() {
|
||||||
@ -34,9 +35,26 @@ function drawAccountChart() {
|
|||||||
|
|
||||||
|
|
||||||
// draw it!
|
// draw it!
|
||||||
drawChart('#' + holderID, URL, opt);
|
drawChart('#' + holderID, URL, 'LineChart', opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
//var URL = 'chart/home';
|
//var URL = 'chart/home';
|
||||||
//drawChart('#chart',URL,opt);
|
//drawChart('#chart',URL,opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawExtraCharts() {
|
||||||
|
|
||||||
|
var opt = {
|
||||||
|
legend: {
|
||||||
|
position: 'none'
|
||||||
|
},
|
||||||
|
chartArea: {
|
||||||
|
width: 300,
|
||||||
|
height: 300
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
drawChart('#budgetChart', 'chart/home/budgets', 'PieChart', opt);
|
||||||
|
drawChart('#categoryChart', 'chart/home/categories','PieChart', opt);
|
||||||
|
drawChart('#beneficiaryChart', 'chart/home/beneficiaries','PieChart', opt);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user