All “all” views are now consistent #595

This commit is contained in:
James Cole 2017-04-22 07:04:39 +02:00
parent ae30f7920b
commit ee08fc2421
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
14 changed files with 397 additions and 42 deletions

View File

@ -311,7 +311,7 @@ class BudgetController extends Controller
$journals->setPath('/budgets/show/' . $budget->id);
$subTitle = e($budget->name);
$subTitle = trans('firefly.all_journals_for_budget', ['name' => $budget->name]);
return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle'));
}

View File

@ -18,11 +18,14 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Navigation;
@ -153,6 +156,143 @@ class BudgetController extends Controller
return Response::json($data);
}
/**
* @param Budget $budget
* @param BudgetLimit|null $budgetLimit
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseAsset(Budget $budget, BudgetLimit $budgetLimit = null)
{
$cache = new CacheProperties;
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimit->id ?? 0);
$cache->addProperty('chart.budget.expense-asset');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget);
if (!is_null($budgetLimit->id)) {
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
}
$transactions = $collector->getJournals();
$result = [];
$chartData = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$assetId = intval($transaction->account_id);
$result[$assetId] = $result[$assetId] ?? '0';
$result[$assetId] = bcadd($transaction->transaction_amount, $result[$assetId]);
}
$names = $this->getAccountNames(array_keys($result));
foreach ($result as $assetId => $amount) {
$chartData[$names[$assetId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Budget $budget
* @param BudgetLimit|null $budgetLimit
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseCategory(Budget $budget, BudgetLimit $budgetLimit = null)
{
$cache = new CacheProperties;
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimit->id ?? 0);
$cache->addProperty('chart.budget.expense-category');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withCategoryInformation();
if (!is_null($budgetLimit->id)) {
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
}
$transactions = $collector->getJournals();
$result = [];
$chartData = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$jrnlCatId = intval($transaction->transaction_journal_category_id);
$transCatId = intval($transaction->transaction_category_id);
$categoryId = max($jrnlCatId, $transCatId);
$result[$categoryId] = $result[$categoryId] ?? '0';
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
}
$names = $this->getCategoryNames(array_keys($result));
foreach ($result as $categoryId => $amount) {
$chartData[$names[$categoryId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* @param Budget $budget
* @param BudgetLimit|null $budgetLimit
*
* @return \Illuminate\Http\JsonResponse
*/
public function expenseExpense(Budget $budget, BudgetLimit $budgetLimit = null)
{
$cache = new CacheProperties;
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimit->id ?? 0);
$cache->addProperty('chart.budget.expense-expense');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setTypes([TransactionType::WITHDRAWAL])->setBudget($budget)->withOpposingAccount();
if (!is_null($budgetLimit->id)) {
$collector->setRange($budgetLimit->start_date, $budgetLimit->end_date);
}
$transactions = $collector->getJournals();
$result = [];
$chartData = [];
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$opposingId = intval($transaction->opposing_account_id);
$result[$opposingId] = $result[$opposingId] ?? '0';
$result[$opposingId] = bcadd($transaction->transaction_amount, $result[$opposingId]);
}
$names = $this->getAccountNames(array_keys($result));
foreach ($result as $opposingId => $amount) {
$chartData[$names[$opposingId]] = $amount;
}
$data = $this->generator->pieChart($chartData);
$cache->store($data);
return Response::json($data);
}
/**
* Shows a budget list with spent/left/overspent.
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five.
@ -288,6 +428,27 @@ class BudgetController extends Controller
return Response::json($data);
}
/**
* @param array $accountIds
*
* @return array
*/
private function getAccountNames(array $accountIds): array
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::EXPENSE]);
$grouped = $accounts->groupBy('id')->toArray();
$return = [];
foreach ($accountIds as $accountId) {
if (isset($grouped[$accountId])) {
$return[$accountId] = $grouped[$accountId][0]['name'];
}
}
return $return;
}
/**
* @param Budget $budget
* @param Carbon $start
@ -314,6 +475,30 @@ class BudgetController extends Controller
return $budgeted;
}
/**
* Small helper function for some of the charts.
*
* @param array $categoryIds
*
* @return array
*/
private function getCategoryNames(array $categoryIds): array
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$categories = $repository->getCategories();
$grouped = $categories->groupBy('id')->toArray();
$return = [];
foreach ($categoryIds as $categoryId) {
if (isset($grouped[$categoryId])) {
$return[$categoryId] = $grouped[$categoryId][0]['name'];
}
}
$return[0] = trans('firefly.noCategory');
return $return;
}
/**
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok.

View File

@ -210,10 +210,11 @@ class PiggyBankController extends Controller
$end = session('end', Carbon::now()->endOfMonth());
$accounts = [];
Log::debug('Looping piggues');
/** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) {
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount;
$piggyBank->percentage = $piggyBank->savedSoFar != 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
$piggyBank->percentage = bccomp('0', $piggyBank->savedSoFar) !== 0 ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
$piggyBank->percentage = $piggyBank->percentage > 100 ? 100 : $piggyBank->percentage;

View File

@ -243,6 +243,8 @@ class TagController extends Controller
$start = null;
$end = null;
$periods = new Collection;
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
$sum = '0';
// prep for "all" view.
@ -250,6 +252,7 @@ class TagController extends Controller
$subTitle = trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
$start = $repository->firstUseDate($tag);
$end = new Carbon;
$sum = $repository->sumOfTag($tag);
}
// prep for "specific date" view.
@ -300,9 +303,8 @@ class TagController extends Controller
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
}
$sum = '0';
return view('tags.show', compact('tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
return view('tags.show', compact('apiKey','tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
}

View File

@ -246,6 +246,28 @@ class TagRepository implements TagRepositoryInterface
}
/**
* @param Tag $tag
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return string
*/
public function sumOfTag(Tag $tag, Carbon $start = null, Carbon $end = null): string
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
if (!is_null($start) && !is_null($end)) {
$collector->setRange($start, $end);
}
$collector->setAllAssetAccounts()->setTag($tag);
$sum = $collector->getJournals()->sum('transaction_amount');
return strval($sum);
}
/**
* Can a tag become an advance payment?
*

View File

@ -126,6 +126,15 @@ interface TagRepositoryInterface
*/
public function store(array $data): Tag;
/**
* @param Tag $tag
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return string
*/
public function sumOfTag(Tag $tag, Carbon $start = null, Carbon $end = null): string;
/**
* @param Tag $tag
*

View File

@ -19,4 +19,12 @@ $(function () {
columnChart(budgetChartUri, 'budgetOverview');
}
// other three charts:
pieChart(expenseCategoryUri, 'budget-cat-out');
pieChart(expenseAssetUri, 'budget-asset-out');
pieChart(expenseExpenseUri, 'budget-expense-out');
});

42
public/js/ff/tags/show.js Normal file
View File

@ -0,0 +1,42 @@
/*
* show.js
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
/*
Some vars as prep for the map:
*/
var map;
var markers = [];
var setTag = false;
var mapOptions = {
zoom: zoomLevel,
center: new google.maps.LatLng(latitude, longitude),
disableDefaultUI: true,
zoomControl: false,
scaleControl: true,
draggable: false
};
function initialize() {
"use strict";
if (doPlaceMarker === true) {
/*
Create new map:
*/
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var myLatlng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
position: myLatlng,
map: map
});
marker.setMap(map);
}
}
google.maps.event.addDomListener(window, 'load', initialize);

File diff suppressed because one or more lines are too long

View File

@ -92,9 +92,12 @@ return [
'expenses_by_category' => 'Expenses by category',
'expenses_by_budget' => 'Expenses by budget',
'income_by_category' => 'Income by category',
'expenses_by_asset_account' => 'Expenses by asset account',
'expenses_by_expense_account' => 'Expenses by expense account',
'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.',
'sum_of_expenses' => 'Sum of expenses',
'sum_of_income' => 'Sum of income',
'total_sum' => 'Total sum',
'spent_in_specific_budget' => 'Spent in budget ":budget"',
'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"',
'left_in_budget_limit' => 'Left to spend according to budgeting',
@ -121,6 +124,7 @@ return [
'all_journals_without_category' => 'All transactions without a category',
'journals_without_category' => 'Transactions without a category',
'all_journals_for_account' => 'All transactions for account :name',
'chart_all_journals_for_account' => 'Chart of all transactions for account :name',
'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
'transferred' => 'Transferred',
'all_withdrawal' => 'All expenses',
@ -134,6 +138,8 @@ return [
'all_journals_for_tag' => 'All transactions for tag ":tag"',
'title_transfer_between' => 'All transfers between :start and :end',
'all_journals_for_category' => 'All transactions for category :name',
'all_journals_for_budget' => 'All transactions for budget :name',
'chart_all_journals_for_budget' => 'Chart of all transactions for budget :name',
'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end',
'not_available_demo_user' => 'The feature you try to access is not available to demo users.',

View File

@ -10,7 +10,11 @@
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ subTitle }}
{% if moment == 'all' %}
{{ trans('firefly.chart_all_journals_for_account', {name:account.name}) }}
{% else %}
xxx
{% endif %}
</h3>
<!-- ACTIONS MENU -->
<div class="box-tools pull-right">
@ -145,13 +149,13 @@
// uri's for charts:
var chartUri = '{{ chartUri }}';
{% if start and end %}
var incomeCategoryUri = '{{ route('chart.account.income-category', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
var expenseCategoryUri = '{{ route('chart.account.expense-category', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
var expenseBudgetUri = '{{ route('chart.account.expense-budget', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
var incomeCategoryUri = '{{ route('chart.account.income-category', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
var expenseCategoryUri = '{{ route('chart.account.expense-category', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
var expenseBudgetUri = '{{ route('chart.account.expense-budget', [account.id, start.format('Ymd'), end.format('Ymd')]) }}';
{% else %}
var incomeCategoryUri = '{{ route('chart.account.income-category', [account.id, 'all', 'all']) }}';
var expenseCategoryUri = '{{ route('chart.account.expense-category', [account.id, 'all', 'all']) }}';
var expenseBudgetUri = '{{ route('chart.account.expense-budget', [account.id, 'all', 'all']) }}';
var incomeCategoryUri = '{{ route('chart.account.income-category', [account.id, 'all', 'all']) }}';
var expenseCategoryUri = '{{ route('chart.account.expense-category', [account.id, 'all', 'all']) }}';
var expenseBudgetUri = '{{ route('chart.account.expense-budget', [account.id, 'all', 'all']) }}';
{% endif %}
</script>

View File

@ -9,7 +9,13 @@
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'overview'|_ }}</h3>
<h3 class="box-title">
{% if budgetLimit %}
XXXX
{% else %}
{{ trans('firefly.chart_all_journals_for_budget', {name:budget.name}) }}
{% endif %}
</h3>
<!-- ACTIONS MENU -->
<div class="box-tools pull-right">
@ -29,6 +35,45 @@
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'expenses_by_category'|_ }}</h3>
</div>
<div class="box-body">
<div style="width:60%;margin:0 auto;">
<canvas id="budget-cat-out" style="margin:0 auto;"></canvas>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'expenses_by_asset_account'|_ }}</h3>
</div>
<div class="box-body">
<div style="width:60%;margin:0 auto;">
<canvas id="budget-asset-out" style="margin:0 auto;"></canvas>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'expenses_by_expense_account'|_ }}</h3>
</div>
<div class="box-body">
<div style="width:60%;margin:0 auto;">
<canvas id="budget-expense-out" style="margin:0 auto;"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-offset-9 col-lg-3 col-md-offset-9 col-md-3 col-sm-12 col-xs-12">
<p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">{{ 'showEverything'|_ }}</a></p>
@ -45,12 +90,12 @@
<div class="box-body">
{% include 'list.journals-tasker' with {hideBudgets:true, hideBills:true} %}
{% if budgetLimit %}
<p>
<i class="fa fa-calendar" aria-hidden="true"></i>
<a href="{{ route('budgets.show', [budget.id]) }}">
{{ 'show_all_no_filter'|_ }}
</a>
</p>
<p>
<i class="fa fa-calendar" aria-hidden="true"></i>
<a href="{{ route('budgets.show', [budget.id]) }}">
{{ 'show_all_no_filter'|_ }}
</a>
</p>
{% endif %}
</div>
</div>
@ -111,12 +156,19 @@
var budgetID = {{ budget.id }};
var budgetLimitID = 0;
{% if budgetLimit.id %}
budgetLimitID = {{ budgetLimit.id }};
var budgetChartUri = '{{ route('chart.budget.budget-limit', [budget.id, budgetLimit.id] ) }}';
{% else %}
var budgetChartUri = '{{ route('chart.budget.budget', [budget.id] ) }}';
{% endif %}
budgetLimitID = {{ budgetLimit.id }};
var budgetChartUri = '{{ route('chart.budget.budget-limit', [budget.id, budgetLimit.id] ) }}';
var expenseCategoryUri = '{{ route('chart.budget.expense-category', [budget.id, budgetLimit.id]) }}';
var expenseAssetUri = '{{ route('chart.budget.expense-asset', [budget.id, budgetLimit.id]) }}';
var expenseExpenseUri = '{{ route('chart.budget.expense-expense', [budget.id, budgetLimit.id]) }}';
{% else %}
var budgetChartUri = '{{ route('chart.budget.budget', [budget.id] ) }}';
var expenseCategoryUri = '{{ route('chart.budget.expense-category', [budget.id]) }}';
var expenseAssetUri = '{{ route('chart.budget.expense-asset', [budget.id]) }}';
var expenseExpenseUri = '{{ route('chart.budget.expense-expense', [budget.id]) }}';
{% endif %}
</script>
<script type="text/javascript" src="js/lib/Chart.bundle.min.js"></script>

View File

@ -37,10 +37,15 @@
{% if tag.date %}
<p>{{ 'date'|_ }}: {{ tag.date.formatLocalized(monthAndDayFormat) }}</p>
{% endif %}
<p>
{{ 'sum'|_ }}: {{ sum|formatAmount }}<br/>
</p>
{% if moment == 'all' %}
<p>
{{ 'total_sum'|_ }}: {{ sum|formatAmount }}<br/>
</p>
{% else %}
<p>
{{ 'sum'|_ }}: {{ sum|formatAmount }}<br/>
</p>
{% endif %}
</div>
</div>
@ -65,9 +70,7 @@
</div>
<div class="box-body">
{% if tag.latitude and tag.longitude and tag.zoomLevel %}
<p>
<img src="https://maps.googleapis.com/maps/api/staticmap?center={{ tag.latitude }},{{ tag.longitude }}&zoom={{ tag.zoomLevel }}&size=600x300">
</p>
<div id="map-canvas" style="width:100%;height:300px;"></div>
{% else %}
<p>{{ 'no_location_set'|_ }}</p>
{% endif %}
@ -164,4 +167,19 @@
{% endblock %}
{% block scripts %}
<script type="text/javascript">
var latitude = {{ tag.latitude|default("52.3167") }};
var longitude = {{ tag.longitude|default("5.5500") }};
var zoomLevel = {{ tag.zoomLevel|default("6") }};
{% if tag.latitude and tag.longitude and tag.zoomLevel %}
var doPlaceMarker = true;
{% else %}
var doPlaceMarker = false;
{% endif %}
var apiKey = "{{ apiKey }}";
</script>
<script src="https://maps.googleapis.com/maps/api/js?v=3&amp;key={{ apiKey }}"></script>
<script src="js/ff/tags/show.js"></script>
{% endblock %}

View File

@ -9,7 +9,7 @@
* See the LICENSE file for details.
*/
declare(strict_types = 1);
declare(strict_types=1);
/**
@ -28,7 +28,7 @@ Route::group(
// Password Reset Routes...
Route::get('password/reset/{token}', ['uses' => 'Auth\ResetPasswordController@showResetForm', 'as' => 'password.reset']);
Route::post('password/email', ['uses' => 'Auth\ForgotPasswordController@sendResetLinkEmail','as' => 'password.email']);
Route::post('password/email', ['uses' => 'Auth\ForgotPasswordController@sendResetLinkEmail', 'as' => 'password.email']);
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
@ -249,6 +249,10 @@ Route::group(
Route::get('budget/{budget}/{budgetlimit}', ['uses' => 'BudgetController@budgetLimit', 'as' => 'budget-limit']);
Route::get('budget/{budget}', ['uses' => 'BudgetController@budget', 'as' => 'budget']);
// these charts are used in budget/show:
Route::get('expense-category/{budget}/{budgetlimit?}', ['uses' => 'BudgetController@expenseCategory', 'as' => 'expense-category']);
Route::get('expense-asset/{budget}/{budgetlimit?}', ['uses' => 'BudgetController@expenseAsset', 'as' => 'expense-asset']);
Route::get('expense-expense/{budget}/{budgetlimit?}', ['uses' => 'BudgetController@expenseExpense', 'as' => 'expense-expense']);
// these charts are used in reports (category reports):
Route::get(
@ -335,7 +339,8 @@ Route::group(
'budget/expense/{accountList}/{tagList}/{start_date}/{end_date}',
['uses' => 'TagReportController@budgetExpense', 'as' => 'budget-expense']
);
Route::get('category/expense/{accountList}/{tagList}/{start_date}/{end_date}',
Route::get(
'category/expense/{accountList}/{tagList}/{start_date}/{end_date}',
['uses' => 'TagReportController@categoryExpense', 'as' => 'category-expense']
);