First code for #159 popups

This commit is contained in:
James Cole 2016-04-01 16:06:55 +02:00
parent fa38c975b6
commit 144a6130f2
11 changed files with 295 additions and 208 deletions

View 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;
}
}

View File

@ -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']);
}
);

View File

@ -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".

View File

@ -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".

View 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');
}

View File

@ -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);
}

View File

@ -0,0 +1,78 @@
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>&nbsp;</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 }}

View 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">&times;</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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}