mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
First code for #159 popups
This commit is contained in:
parent
fa38c975b6
commit
144a6130f2
106
app/Http/Controllers/Popup/ReportController.php
Normal file
106
app/Http/Controllers/Popup/ReportController.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* ReportController.php
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Popup;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Support\Binder\AccountList;
|
||||
use Illuminate\Http\Request;
|
||||
use InvalidArgumentException;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Class ReportController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Popup
|
||||
*/
|
||||
class ReportController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function info(Request $request)
|
||||
{
|
||||
$attributes = $request->get('attributes');
|
||||
$attributes = $this->parseAttributes($attributes);
|
||||
$html = '';
|
||||
switch ($attributes['location']) {
|
||||
default:
|
||||
throw new FireflyException('Firefly cannot handle "' . e($attributes['location']) . '" ');
|
||||
case 'budget-spent-amount':
|
||||
$html = $this->budgetSpentAmount($attributes);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return Response::json(['html' => $html]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function budgetSpentAmount(array $attributes): string
|
||||
{
|
||||
// need to find the budget
|
||||
// then search for expenses in the given period
|
||||
// list them in some table format.
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$budget = $repository->find(intval($attributes['budgetId']));
|
||||
if (is_null($budget->id)) {
|
||||
throw new FireflyException('Could not find a budget for value "' . e($attributes['budgetId']) . '".');
|
||||
}
|
||||
|
||||
// get all expenses in budget in period:
|
||||
$journals = $repository->getExpenses($budget, $attributes['accounts'], $attributes['startDate'], $attributes['endDate']);
|
||||
|
||||
$view = view('popup.report.budget-spent-amount', compact('journals'))->render();
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function parseAttributes(array $attributes): array
|
||||
{
|
||||
$attributes['location'] = $attributes['location'] ?? '';
|
||||
$attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', '');
|
||||
try {
|
||||
$attributes['startDate'] = Carbon::createFromFormat('Ymd', $attributes['startDate']);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new FireflyException('Could not parse start date "' . e($attributes['startDate']) . '".');
|
||||
}
|
||||
|
||||
try {
|
||||
$attributes['endDate'] = Carbon::createFromFormat('Ymd', $attributes['endDate']);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new FireflyException('Could not parse start date "' . e($attributes['endDate']) . '".');
|
||||
}
|
||||
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
}
|
@ -378,6 +378,14 @@ Route::group(
|
||||
Route::post('/transaction/destroy/{tj}', ['uses' => 'TransactionController@destroy', 'as' => 'transactions.destroy']);
|
||||
Route::post('/transaction/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']);
|
||||
|
||||
/**
|
||||
* POPUP Controllers
|
||||
*/
|
||||
/**
|
||||
* Report popup
|
||||
*/
|
||||
Route::get('/popup/report', ['uses' => 'Popup\ReportController@info', 'as' => 'popup.report']);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -372,6 +372,30 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all expenses for the given budget and the given accounts, in the given period.
|
||||
*
|
||||
* @param Budget $budget
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getExpenses(Budget $budget, Collection $accounts, Carbon $start, Carbon $end):Collection
|
||||
{
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
$set = $budget->transactionjournals()
|
||||
->before($end)
|
||||
->after($start)
|
||||
->expanded()
|
||||
->where('transaction_types.type', 'Withdrawal')
|
||||
->whereIn('source_account.id', $ids)
|
||||
->get(TransactionJournal::QUERYFIELDS);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expenses for this budget grouped per day, with the date
|
||||
* in "date" (a string, not a Carbon) and the amount in "dailyAmount".
|
||||
|
@ -132,6 +132,18 @@ interface BudgetRepositoryInterface
|
||||
*/
|
||||
public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Returns all expenses for the given budget and the given accounts, in the given period.
|
||||
*
|
||||
* @param Budget $budget
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getExpenses(Budget $budget, Collection $accounts, Carbon $start, Carbon $end):Collection;
|
||||
|
||||
/**
|
||||
* Returns the expenses for this budget grouped per day, with the date
|
||||
* in "date" (a string, not a Carbon) and the amount in "dailyAmount".
|
||||
|
49
public/js/reports/default/all.js
Normal file
49
public/js/reports/default/all.js
Normal file
@ -0,0 +1,49 @@
|
||||
/* globals startDate, endDate, reportType, accountIds */
|
||||
/*
|
||||
* all.js
|
||||
* Copyright (C) 2016 Sander Dorigo
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by sander on 01/04/16.
|
||||
*/
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
// find the little info buttons and respond to them.
|
||||
$('.firefly-info-button').click(clickInfoButton);
|
||||
|
||||
});
|
||||
|
||||
function clickInfoButton(e) {
|
||||
"use strict";
|
||||
// find all data tags, regardless of what they are:
|
||||
var element = $(e.target);
|
||||
var attributes = element.data();
|
||||
|
||||
// add some more elements:
|
||||
attributes.startDate = startDate;
|
||||
attributes.endDate = endDate;
|
||||
attributes.reportType = reportType;
|
||||
attributes.accounts = accountIds;
|
||||
|
||||
console.log(attributes);
|
||||
$.getJSON('popup/report', {attributes: attributes}).success(respondInfoButton).fail(errorInfoButton);
|
||||
}
|
||||
|
||||
function errorInfoButton(data) {
|
||||
"use strict";
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
function respondInfoButton(data) {
|
||||
"use strict";
|
||||
console.log(123);
|
||||
$('#defaultModal').empty().html(data.html);
|
||||
$('#defaultModal').modal('show');
|
||||
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, expenseRestShow:true, incomeRestShow:true, year, month, hideTheRest, showTheRest, showTheRestExpense, hideTheRestExpense, columnChart, lineChart, stackedColumnChart */
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
drawChart();
|
||||
|
||||
if ($('#inputDateRange').length > 0) {
|
||||
|
||||
picker = $('#inputDateRange').daterangepicker(
|
||||
{
|
||||
locale: {
|
||||
format: 'YYYY-MM-DD',
|
||||
firstDay: 1,
|
||||
},
|
||||
minDate: minDate,
|
||||
drops: 'up',
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// set values from cookies, if any:
|
||||
if (readCookie('report-type') !== null) {
|
||||
$('select[name="report_type"]').val(readCookie('report-type'));
|
||||
}
|
||||
|
||||
if ((readCookie('report-accounts') !== null)) {
|
||||
var arr = readCookie('report-accounts').split(',');
|
||||
arr.forEach(function (val) {
|
||||
$('input[type="checkbox"][value="' + val + '"]').prop('checked', true);
|
||||
});
|
||||
}
|
||||
|
||||
// set date:
|
||||
var startStr = readCookie('report-start');
|
||||
var endStr = readCookie('report-end');
|
||||
if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) {
|
||||
var startDate = moment(startStr, "YYYYMMDD");
|
||||
var endDate = moment(endStr, "YYYYMMDD");
|
||||
var datePicker = $('#inputDateRange').data('daterangepicker');
|
||||
datePicker.setStartDate(startDate);
|
||||
datePicker.setEndDate(endDate);
|
||||
}
|
||||
}
|
||||
|
||||
$('.openModal').on('click', openModal);
|
||||
|
||||
$('.date-select').on('click', preSelectDate);
|
||||
|
||||
$('#report-form').on('submit', catchSubmit);
|
||||
|
||||
|
||||
// click open the top X income list:
|
||||
$('#showIncomes').click(showIncomes);
|
||||
// click open the top X expense list:
|
||||
$('#showExpenses').click(showExpenses);
|
||||
});
|
||||
|
||||
function catchSubmit() {
|
||||
"use strict";
|
||||
// default;20141201;20141231;4;5
|
||||
// report name:
|
||||
var url = '' + $('select[name="report_type"]').val() + '/';
|
||||
|
||||
// date, processed:
|
||||
var picker = $('#inputDateRange').data('daterangepicker');
|
||||
url += moment(picker.startDate).format("YYYYMMDD") + '/';
|
||||
url += moment(picker.endDate).format("YYYYMMDD") + '/';
|
||||
|
||||
// all account ids:
|
||||
var count = 0;
|
||||
var accounts = [];
|
||||
$.each($('.account-checkbox'), function (i, v) {
|
||||
var c = $(v);
|
||||
if (c.prop('checked')) {
|
||||
url += c.val() + ';';
|
||||
accounts.push(c.val());
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count > 0) {
|
||||
// set cookie to remember choices.
|
||||
createCookie('report-type', $('select[name="report_type"]').val(), 365);
|
||||
createCookie('report-accounts', accounts, 365);
|
||||
createCookie('report-start', moment(picker.startDate).format("YYYYMMDD"), 365);
|
||||
createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365);
|
||||
|
||||
window.location.href = reportURL + "/" + url;
|
||||
}
|
||||
//console.log(url);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function preSelectDate(e) {
|
||||
"use strict";
|
||||
var link = $(e.target);
|
||||
var picker = $('#inputDateRange').data('daterangepicker');
|
||||
picker.setStartDate(link.data('start'));
|
||||
picker.setEndDate(link.data('end'));
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function drawChart() {
|
||||
"use strict";
|
||||
if (typeof columnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
|
||||
columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
|
||||
columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
|
||||
}
|
||||
if (typeof stackedColumnChart !== 'undefined' && typeof year !== 'undefined' && typeof month === 'undefined') {
|
||||
stackedColumnChart('chart/budget/year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'budgets');
|
||||
stackedColumnChart('chart/category/spent-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-spent-in-year');
|
||||
stackedColumnChart('chart/category/earned-in-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'categories-earned-in-year');
|
||||
}
|
||||
|
||||
if (typeof lineChart !== 'undefined' && typeof accountIds !== 'undefined') {
|
||||
lineChart('chart/account/report/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'account-balances-chart');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function openModal(e) {
|
||||
"use strict";
|
||||
var target = $(e.target).parent();
|
||||
var URL = target.attr('href');
|
||||
|
||||
$.get(URL).done(function (data) {
|
||||
$('#defaultModal').empty().html(data).modal('show');
|
||||
|
||||
}).fail(function () {
|
||||
alert('Could not load data.');
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function showIncomes() {
|
||||
"use strict";
|
||||
if (incomeRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showIncomes').text(showTheRest);
|
||||
$('.incomesCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showIncomes').text(hideTheRest);
|
||||
$('.incomesCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
incomeRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function showExpenses() {
|
||||
"use strict";
|
||||
if (expenseRestShow) {
|
||||
// hide everything, make button say "show"
|
||||
$('#showExpenses').text(showTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('in').addClass('out');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = false;
|
||||
} else {
|
||||
// show everything, make button say "hide".
|
||||
$('#showExpenses').text(hideTheRestExpense);
|
||||
$('.expenseCollapsed').removeClass('out').addClass('in');
|
||||
|
||||
// toggle:
|
||||
expenseRestShow = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function createCookie(name, value, days) {
|
||||
var expires;
|
||||
|
||||
if (days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/";
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var nameEQ = encodeURIComponent(name) + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function eraseCookie(name) {
|
||||
createCookie(name, "", -1);
|
||||
}
|
78
resources/views/popup/list/journals.twig
Normal file
78
resources/views/popup/list/journals.twig
Normal file
@ -0,0 +1,78 @@
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>{{ trans('list.description') }}</th>
|
||||
<th>{{ trans('list.amount') }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ trans('list.date') }}</th>
|
||||
<th class="hidden-xs">{{ trans('list.from') }}</th>
|
||||
<th class="hidden-xs">{{ trans('list.to') }}</th>
|
||||
<!-- Hide budgets? -->
|
||||
{% if not hideBudgets %}
|
||||
<th class="hidden-xs"><i class="fa fa-tasks fa-fw" title="{{ trans('list.budget') }}"></i></th>
|
||||
{% endif %}
|
||||
|
||||
<!-- Hide categories? -->
|
||||
{% if not hideCategories %}
|
||||
<th class="hidden-xs"><i class="fa fa-bar-chart fa-fw" title="{{ trans('list.category') }}"></i></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for journal in journals %}
|
||||
<tr class="drag" data-date="{{ journal.date.format('Y-m-d') }}" data-id="{{ journal.id }}">
|
||||
<td class="hidden-xs">
|
||||
{{ journal|typeIcon }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('transactions.show',journal.id) }}" title="{{ journal.description }}">{{ journal.description }}</a>
|
||||
{% if journal.attachments|length > 0 %}
|
||||
<i class="fa fa-paperclip pull-right"
|
||||
title="{{ Lang.choice('firefly.nr_of_attachments', journal.attachments|length, {count: journal.attachments|length}) }}"></i>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{ journal|formatJournal }}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
{{ journal.date.formatLocalized(monthAndDayFormat) }}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{% if journal.source_account_type == 'Cash account' %}
|
||||
<span class="text-success">(cash)</span>
|
||||
{% else %}
|
||||
<a href="{{ route('accounts.show',journal.source_account_id) }}">{{ journal.source_account_name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{% if journal.destination_account_type == 'Cash account' %}
|
||||
<span class="text-success">(cash)</span>
|
||||
{% else %}
|
||||
<a href="{{ route('accounts.show',journal.destination_account_id) }}">{{ journal.destination_account_name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<!-- Do NOT hide the budget? -->
|
||||
{% if not hideBudgets %}
|
||||
<td class="hidden-xs">
|
||||
{% if journal.budgets[0] %}
|
||||
<a href="{{ route('budgets.show',journal.budgets[0].id) }}">{{ journal.budgets[0].name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
<!-- Do NOT hide the category? -->
|
||||
{% if not hideCategories %}
|
||||
<td class="hidden-xs">
|
||||
{% if journal.categories[0] %}
|
||||
<a href="{{ route('categories.show',journal.categories[0].id) }}">{{ journal.categories[0].name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ journals.render|raw }}
|
15
resources/views/popup/report/budget-spent-amount.twig
Normal file
15
resources/views/popup/report/budget-spent-amount.twig
Normal file
@ -0,0 +1,15 @@
|
||||
<!-- Modal dialog to show budget expenses -->
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{ 'close'|_ }}"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="testTriggerLabel">{{ 'budget_spent_amount'|_ }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% include 'popup/list/journals.twig' %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -94,5 +94,6 @@
|
||||
var showTheRestExpense = '{{ trans('firefly.showTheRest',{number:expenseTopLength}) }}';
|
||||
var hideTheRestExpense = '{{ trans('firefly.hideTheRest',{number:expenseTopLength}) }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="js/reports/default/all.js"></script>
|
||||
<script type="text/javascript" src="js/reports/default/month.js"></script>
|
||||
{% endblock %}
|
||||
|
@ -176,5 +176,6 @@
|
||||
var hideTheRestExpense = '{{ trans('firefly.hideTheRest',{number:expenseTopLength}) }}';
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" src="js/reports/default/all.js"></script>
|
||||
<script type="text/javascript" src="js/reports/default/multi-year.js"></script>
|
||||
{% endblock %}
|
||||
|
@ -125,6 +125,7 @@
|
||||
var hideTheRestExpense = '{{ trans('firefly.hideTheRest',{number:expenseTopLength}) }}';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="js/reports/default/all.js"></script>
|
||||
<script type="text/javascript" src="js/reports/default/year.js"></script>
|
||||
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user