mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-30 10:33:30 +00:00
New charts for #2726
This commit is contained in:
@@ -46,7 +46,7 @@ interface GeneratorInterface
|
|||||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||||
* 'entries' =>
|
* 'entries' =>
|
||||||
* [
|
* [
|
||||||
* 'label-of-entry' => 'value'
|
* key => [value => x, 'currency_symbol' => 'x']
|
||||||
* ]
|
* ]
|
||||||
* ]
|
* ]
|
||||||
* 1: [
|
* 1: [
|
||||||
@@ -56,7 +56,7 @@ interface GeneratorInterface
|
|||||||
* 'fill' => if to fill a line? optional, will not be included when unused.
|
* 'fill' => if to fill a line? optional, will not be included when unused.
|
||||||
* 'entries' =>
|
* 'entries' =>
|
||||||
* [
|
* [
|
||||||
* 'label-of-entry' => 'value'
|
* key => [value => x, 'currency_symbol' => 'x']
|
||||||
* ]
|
* ]
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
|
240
app/Http/Controllers/Chart/TransactionController.php
Normal file
240
app/Http/Controllers/Chart/TransactionController.php
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransactionController.php
|
||||||
|
* Copyright (c) 2020 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Chart;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||||
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransactionController
|
||||||
|
*/
|
||||||
|
class TransactionController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var GeneratorInterface Chart generation methods. */
|
||||||
|
protected $generator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TransactionController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->generator = app(GeneratorInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $objectType
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function budgets(Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty('chart.transactions.budgets');
|
||||||
|
if ($cache->has()) {
|
||||||
|
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
$collector->withBudgetInformation();
|
||||||
|
$collector->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
|
||||||
|
$result = $collector->getExtractedJournals();
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
// group by category.
|
||||||
|
/** @var array $journal */
|
||||||
|
foreach ($result as $journal) {
|
||||||
|
$budget = $journal['budget_name'] ?? (string)trans('firefly.no_budget');
|
||||||
|
$title = sprintf('%s (%s)', $budget, $journal['currency_symbol']);
|
||||||
|
// key => [value => x, 'currency_symbol' => 'x']
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => '0',
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
|
||||||
|
|
||||||
|
if (null !== $journal['foreign_amount']) {
|
||||||
|
$title = sprintf('%s (%s)', $budget, $journal['foreign_currency_symbol']);
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => $journal['foreign_amount'],
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||||
|
$cache->store($chart);
|
||||||
|
|
||||||
|
return response()->json($chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $objectType
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function categories(string $objectType, Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty($objectType);
|
||||||
|
$cache->addProperty('chart.transactions.categories');
|
||||||
|
if ($cache->has()) {
|
||||||
|
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
$collector->withCategoryInformation();
|
||||||
|
switch ($objectType) {
|
||||||
|
default:
|
||||||
|
throw new FireflyException(sprintf('Cant handle "%s"', $objectType));
|
||||||
|
case 'withdrawal':
|
||||||
|
$collector->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
break;
|
||||||
|
case 'deposit':
|
||||||
|
$collector->setTypes([TransactionType::DEPOSIT]);
|
||||||
|
break;
|
||||||
|
case 'transfers':
|
||||||
|
$collector->setTypes([TransactionType::TRANSFER]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$result = $collector->getExtractedJournals();
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
// group by category.
|
||||||
|
/** @var array $journal */
|
||||||
|
foreach ($result as $journal) {
|
||||||
|
$category = $journal['category_name'] ?? (string)trans('firefly.no_category');
|
||||||
|
$title = sprintf('%s (%s)', $category, $journal['currency_symbol']);
|
||||||
|
// key => [value => x, 'currency_symbol' => 'x']
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => '0',
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
|
||||||
|
|
||||||
|
if (null !== $journal['foreign_amount']) {
|
||||||
|
$title = sprintf('%s (%s)', $category, $journal['foreign_currency_symbol']);
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => $journal['foreign_amount'],
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||||
|
$cache->store($chart);
|
||||||
|
|
||||||
|
return response()->json($chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $objectType
|
||||||
|
* @param Carbon $start
|
||||||
|
* @param Carbon $end
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function destinationAccounts(string $objectType, Carbon $start, Carbon $end)
|
||||||
|
{
|
||||||
|
$cache = new CacheProperties;
|
||||||
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty($objectType);
|
||||||
|
$cache->addProperty('chart.transactions.destinations');
|
||||||
|
if ($cache->has()) {
|
||||||
|
//return response()->json($cache->get()); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setRange($start, $end);
|
||||||
|
$collector->withAccountInformation();
|
||||||
|
switch ($objectType) {
|
||||||
|
default:
|
||||||
|
throw new FireflyException(sprintf('Cant handle "%s"', $objectType));
|
||||||
|
case 'withdrawal':
|
||||||
|
$collector->setTypes([TransactionType::WITHDRAWAL]);
|
||||||
|
break;
|
||||||
|
case 'deposit':
|
||||||
|
$collector->setTypes([TransactionType::DEPOSIT]);
|
||||||
|
break;
|
||||||
|
case 'transfers':
|
||||||
|
$collector->setTypes([TransactionType::TRANSFER]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$result = $collector->getExtractedJournals();
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
// group by category.
|
||||||
|
/** @var array $journal */
|
||||||
|
foreach ($result as $journal) {
|
||||||
|
$name = $journal['destination_account_name'];
|
||||||
|
$title = sprintf('%s (%s)', $name, $journal['currency_symbol']);
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => '0',
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
|
||||||
|
|
||||||
|
if (null !== $journal['foreign_amount']) {
|
||||||
|
$title = sprintf('%s (%s)', $name, $journal['foreign_currency_symbol']);
|
||||||
|
$data[$title] = $data[$title] ?? [
|
||||||
|
'amount' => $journal['foreign_amount'],
|
||||||
|
'currency_symbol' => $journal['currency_symbol'],
|
||||||
|
];
|
||||||
|
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$chart = $this->generator->multiCurrencyPieChart($data);
|
||||||
|
$cache->store($chart);
|
||||||
|
|
||||||
|
return response()->json($chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
public/v1/js/ff/transactions/index.js
vendored
Normal file
28
public/v1/js/ff/transactions/index.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* show.js
|
||||||
|
* Copyright (c) 2019 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
"use strict";
|
||||||
|
if (!showAll) {
|
||||||
|
multiCurrencyPieChart(categoryChartUri, 'category_chart');
|
||||||
|
multiCurrencyPieChart(budgetChartUri, 'budget_chart');
|
||||||
|
multiCurrencyPieChart(destinationChartUri, 'destination_chart');
|
||||||
|
}
|
||||||
|
});
|
@@ -218,6 +218,7 @@ return [
|
|||||||
'unpaid_in_currency' => 'Unpaid in :currency',
|
'unpaid_in_currency' => 'Unpaid in :currency',
|
||||||
'is_alpha_warning' => 'You are running an ALPHA version. Be wary of bugs and issues.',
|
'is_alpha_warning' => 'You are running an ALPHA version. Be wary of bugs and issues.',
|
||||||
'is_beta_warning' => 'You are running an BETA version. Be wary of bugs and issues.',
|
'is_beta_warning' => 'You are running an BETA version. Be wary of bugs and issues.',
|
||||||
|
'all_destination_accounts' => 'Destination accounts',
|
||||||
|
|
||||||
// check for updates:
|
// check for updates:
|
||||||
'update_check_title' => 'Check for updates',
|
'update_check_title' => 'Check for updates',
|
||||||
|
@@ -15,6 +15,51 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if periods|length > 0 %}
|
||||||
|
|
||||||
|
{% set boxSize = 'col-lg-6 col-md-6 col-sm-12 col-xs-12' %}
|
||||||
|
{% if objectType == 'withdrawal' %}
|
||||||
|
{% set boxSize = 'col-lg-4 col-md-6 col-sm-12 col-xs-12' %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="row">
|
||||||
|
{# for withdrawals, deposits and transfers #}
|
||||||
|
<div class="{{ boxSize }}">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'categories'|_ }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<canvas id="category_chart" style="width:100%;height:250px;" height="250"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# only for withdrawals #}
|
||||||
|
{% if objectType == 'withdrawal' %}
|
||||||
|
<div class="{{ boxSize }}">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'budgets'|_ }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<canvas id="budget_chart" style="width:100%;height:250px;" height="250"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{# for all #}
|
||||||
|
<div class="{{ boxSize }}">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'all_destination_accounts'|_ }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<canvas id="destination_chart" style="width:100%;height:250px;" height="250"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{# list with journals #}
|
{# list with journals #}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="{% if periods|length > 0 %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}">
|
<div class="{% if periods|length > 0 %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}">
|
||||||
@@ -68,4 +113,18 @@
|
|||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{# required for groups.twig #}
|
{# required for groups.twig #}
|
||||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||||
|
var showAll = {% if periods|length > 0 %}false{% else %}true{% endif %};
|
||||||
|
|
||||||
|
var categoryChartUri = '{{ route('chart.transactions.categories', [objectType, start.format('Y-m-d'), end.format('Y-m-d')]) }}';
|
||||||
|
var budgetChartUri = '{{ route('chart.transactions.budgets', [start.format('Y-m-d'), end.format('Y-m-d')]) }}';
|
||||||
|
var destinationChartUri = '{{ route('chart.transactions.destinationAccounts', [objectType, start.format('Y-m-d'), end.format('Y-m-d')]) }}';
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" src="v1/js/lib/chartjs-plugin-annotation.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="v1/js/ff/transactions/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -488,6 +488,21 @@ Route::group(
|
|||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chart\Transactions Controller
|
||||||
|
*/
|
||||||
|
Route::group(
|
||||||
|
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Chart', 'prefix' => 'chart/transactions', 'as' => 'chart.transactions.'],
|
||||||
|
static function () {
|
||||||
|
Route::get('categories/{objectType}/{start_date}/{end_date}', ['uses' => 'TransactionController@categories', 'as' => 'categories']);
|
||||||
|
Route::get('budgets/{start_date}/{end_date}', ['uses' => 'TransactionController@budgets', 'as' => 'budgets']);
|
||||||
|
Route::get('destinationAccounts/{objectType}/{start_date}/{end_date}', ['uses' => 'TransactionController@destinationAccounts', 'as' => 'destinationAccounts']);
|
||||||
|
//
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export controller
|
* Export controller
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user