First working example of category report. No content, just place holders. #396

This commit is contained in:
James Cole 2016-11-10 06:23:21 +01:00
parent 8583b574ac
commit 5d4f1bc76d
15 changed files with 345 additions and 196 deletions

View File

@ -111,6 +111,15 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -0,0 +1,99 @@
<?php
/**
* MonthReportGenerator.php
* Copyright (C) 2016 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.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use Illuminate\Support\Collection;
/**
* Class MonthReportGenerator
*
* @package FireflyIII\Generator\Report\Category
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Collection */
private $categories;
/** @var Carbon */
private $end;
/** @var Carbon */
private $start;
/**
* @return string
*/
public function generate(): string
{
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$reportType = 'category';
// render!
return view('reports.category.month', compact('accountIds', 'reportType'))
->with('start', $this->start)->with('end', $this->end)
->with('categories', $this->categories)
->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
$this->categories = $categories;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* MultiYearReportGenerator.php
* Copyright (C) 2016 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.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
/**
* Class MultiYearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@ -0,0 +1,28 @@
<?php
/**
* YearReportGenerator.php
* Copyright (C) 2016 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.
*/
declare(strict_types = 1);
namespace FireflyIII\Generator\Report\Category;
/**
* Class YearReportGenerator
*
* @package FireflyIII\Generator\Report\Audit
*/
class YearReportGenerator extends MonthReportGenerator
{
/**
* Doesn't do anything different.
*/
}

View File

@ -55,6 +55,6 @@ class ReportGeneratorFactory
return $obj; return $obj;
} }
throw new FireflyException(sprintf('Class "%s" does not exist.', $class)); throw new FireflyException(sprintf('Cannot generate report. There is no "%s"-report for period "%s".', $type, $period));
} }
} }

View File

@ -36,6 +36,13 @@ interface ReportGeneratorInterface
*/ */
public function setAccounts(Collection $accounts): ReportGeneratorInterface; public function setAccounts(Collection $accounts): ReportGeneratorInterface;
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface;
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -87,4 +87,13 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
}
} }

View File

@ -16,7 +16,6 @@ namespace FireflyIII\Generator\Report\Standard;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@ -61,6 +60,15 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -16,7 +16,6 @@ namespace FireflyIII\Generator\Report\Standard;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@ -61,6 +60,15 @@ class YearReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -23,15 +23,11 @@ use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use stdClass; use stdClass;
@ -45,20 +41,16 @@ class ReportHelper implements ReportHelperInterface
/** @var BudgetRepositoryInterface */ /** @var BudgetRepositoryInterface */
protected $budgetRepository; protected $budgetRepository;
/** @var TagRepositoryInterface */
protected $tagRepository;
/** /**
* ReportHelper constructor. * ReportHelper constructor.
* *
* *
* @param BudgetRepositoryInterface $budgetRepository * @param BudgetRepositoryInterface $budgetRepository
* @param TagRepositoryInterface $tagRepository
*/ */
public function __construct(BudgetRepositoryInterface $budgetRepository, TagRepositoryInterface $tagRepository) public function __construct(BudgetRepositoryInterface $budgetRepository)
{ {
$this->budgetRepository = $budgetRepository; $this->budgetRepository = $budgetRepository;
$this->tagRepository = $tagRepository;
} }
/** /**
@ -234,78 +226,4 @@ class ReportHelper implements ReportHelperInterface
return $months; return $months;
} }
/**
* Returns an array of tags and their comparitive size with amounts bla bla.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array
{
$ids = $accounts->pluck('id')->toArray();
$set = Tag::
leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin(
'transactions AS source', function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0');
}
)
->leftJoin(
'transactions AS destination', function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', '0');
}
)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where(
// source.account_id in accountIds XOR destination.account_id in accountIds
function (Builder $query) use ($ids) {
$query->where(
function (Builder $q1) use ($ids) {
$q1->whereIn('source.account_id', $ids)
->whereNotIn('destination.account_id', $ids);
}
)->orWhere(
function (Builder $q2) use ($ids) {
$q2->whereIn('destination.account_id', $ids)
->whereNotIn('source.account_id', $ids);
}
);
}
)
->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']);
$collection = [];
if ($set->count() === 0) {
return $collection;
}
/** @var Tag $entry */
foreach ($set as $entry) {
// less than zero? multiply to be above zero.
$amount = $entry->amount;
$id = intval($entry->id);
$previousAmount = $collection[$id]['amount'] ?? '0';
$collection[$id] = [
'id' => $id,
'tag' => $entry->tag,
'amount' => bcadd($previousAmount, $amount),
];
}
// cleanup collection (match "fonts")
$max = strval(max(array_column($collection, 'amount')));
foreach ($collection as $id => $entry) {
$size = bcdiv($entry['amount'], $max, 4);
if (bccomp($size, '0.25') === -1) {
$size = '0.5';
}
$collection[$id]['fontsize'] = $size;
}
return $collection;
}
} }

View File

@ -80,15 +80,4 @@ interface ReportHelperInterface
*/ */
public function listOfMonths(Carbon $date): array; public function listOfMonths(Carbon $date): array;
/**
* Returns an array of tags and their comparitive size with amounts bla bla.
*
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return array
*/
public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array;
} }

View File

@ -16,22 +16,15 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Report\ReportGeneratorFactory; use FireflyIII\Generator\Report\ReportGeneratorFactory;
use FireflyIII\Generator\Report\Standard\MonthReportGenerator;
use FireflyIII\Generator\Report\StandardReportGenerator;
use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Http\Requests\ReportFormRequest; use FireflyIII\Http\Requests\ReportFormRequest;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences; use Preferences;
use Response; use Response;
use Session;
use Steam;
use View; use View;
/** /**
@ -56,6 +49,7 @@ class ReportController extends Controller
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.reports')); View::share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart'); View::share('mainTitleIcon', 'fa-line-chart');
View::share('subTitleIcon', 'fa-calendar');
$this->helper = app(ReportHelperInterface::class); $this->helper = app(ReportHelperInterface::class);
@ -75,12 +69,9 @@ class ReportController extends Controller
*/ */
public function auditReport(Carbon $start, Carbon $end, Collection $accounts) public function auditReport(Carbon $start, Carbon $end, Collection $accounts)
{ {
// throw an error if necessary.
if ($end < $start) { if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!'); return view('error')->with('message', trans('firefly.end_after_start_date'));
} }
// lower threshold
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');
} }
@ -94,7 +85,7 @@ class ReportController extends Controller
] ]
) )
); );
View::share('subTitleIcon', 'fa-calendar');
$generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end); $generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end);
$generator->setAccounts($accounts); $generator->setAccounts($accounts);
@ -112,14 +103,47 @@ class ReportController extends Controller
* @return string * @return string
* @throws FireflyException * @throws FireflyException
*/ */
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts) public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories)
{ {
// throw an error if necessary.
if ($end < $start) { if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!'); return view('error')->with('message', trans('firefly.end_after_start_date'));
}
if ($start < session('first')) {
$start = session('first');
} }
// lower threshold View::share(
'subTitle', trans(
'firefly.report_category',
[
'start' => $start->formatLocalized($this->monthFormat),
'end' => $end->formatLocalized($this->monthFormat),
]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Category', $start, $end);
$generator->setAccounts($accounts);
$generator->setCategories($categories);
$result = $generator->generate();
return $result;
}
/**
* @param Carbon $start
* @param Carbon $end
* @param Collection $accounts
*
* @return string
* @throws FireflyException
*/
public function defaultReport(Carbon $start, Carbon $end, Collection $accounts)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date'));
}
if ($start < session('first')) { if ($start < session('first')) {
$start = session('first'); $start = session('first');
} }
@ -133,7 +157,6 @@ class ReportController extends Controller
] ]
) )
); );
View::share('subTitleIcon', 'fa-calendar');
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end); $generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
$generator->setAccounts($accounts); $generator->setAccounts($accounts);
@ -155,16 +178,8 @@ class ReportController extends Controller
$start = clone session('first'); $start = clone session('first');
$months = $this->helper->listOfMonths($start); $months = $this->helper->listOfMonths($start);
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data; $customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
// does the user have shared accounts?
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
// get id's for quick links: $accountList = join(',', $accounts->pluck('id')->toArray());
$accountIds = [];
/** @var Account $account */
foreach ($accounts as $account) {
$accountIds [] = $account->id;
}
$accountList = join(',', $accountIds);
return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear')); return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear'));
@ -177,7 +192,6 @@ class ReportController extends Controller
*/ */
public function options(string $reportType) public function options(string $reportType)
{ {
$result = '';
switch ($reportType) { switch ($reportType) {
default: default:
$result = $this->noReportOptions(); $result = $this->noReportOptions();
@ -206,7 +220,7 @@ class ReportController extends Controller
$categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); $categories = join(',', $request->getCategoryList()->pluck('id')->toArray());
if ($end < $start) { if ($end < $start) {
throw new FireflyException('End date cannot be before start date, silly!'); return view('error')->with('message', trans('firefly.end_after_start_date'));
} }
// lower threshold // lower threshold

View File

@ -686,6 +686,8 @@ return [
'reports_extra_options' => 'Extra options', 'reports_extra_options' => 'Extra options',
'report_has_no_extra_options' => 'This report has no extra options', 'report_has_no_extra_options' => 'This report has no extra options',
'reports_submit' => 'View report', 'reports_submit' => 'View report',
'end_after_start_date' => 'End date of report must be after start date.',
'select_category' => 'Select one or more categories.',
// charts: // charts:
'chart' => 'Chart', 'chart' => 'Chart',

View File

@ -0,0 +1,65 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-4">
Summary here. Accounts and categories involved. Summary of in/out
</div>
<div class="col-lg-4">
Pie chart with spending (aka all withdrawals in category). Optional checkbox to include all other transactions.
</div>
<div class="col-lg-4">
Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison).
</div>
</div>
<div class="row">
<div class="col-lg-12">
big chart here
Show income / expenses per period. Differs per report: month = per day, year = per month, multi-year = per year.
In a bar chart, possibly grouped by expense/revenue account.
</div>
</div>
<div class="row">
<div class="col-lg-3">
List of spending (withdrawals) by account, if relevant. Grouped:<br>
BC: 456
AH: 123
order by size
linked to chart
use reset button to reset after chart was clicked.
</div>
<div class="col-lg-3">
List of spending (withdrawals) by transaction, if relevant. Not grouped<br>
more groceries: 456
groceries: 123
ordered by size. top x list?
</div>
<div class="col-lg-6">
Same but for income
</div>
</div>
{% endblock %}
{% block scripts %}
{% endblock %}
{% block styles %}
{% endblock %}

View File

@ -113,10 +113,13 @@
<h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3> <h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<h4>{{ 'quick_link_default_report'|_ }}</h4>
{% for type in ['default','audit'] %}
<h4>{{ ('quick_link_'~type~'_report')|_ }}</h4>
<ul> <ul>
<li> <li>
<a href="{{ route('reports.report.default', <a href="{{ route('reports.report.'~type,
[ [
'currentMonthStart', 'currentMonthStart',
'currentMonthEnd', 'currentMonthEnd',
@ -124,7 +127,7 @@
]) }}">{{ 'report_this_month_quick'|_ }}</a> ]) }}">{{ 'report_this_month_quick'|_ }}</a>
</li> </li>
<li> <li>
<a href="{{ route('reports.report.default', <a href="{{ route('reports.report.'~type,
[ [
'currentYearStart', 'currentYearStart',
'currentYearEnd', 'currentYearEnd',
@ -133,7 +136,7 @@
</li> </li>
{% if customFiscalYear == 1 %} {% if customFiscalYear == 1 %}
<li> <li>
<a href="{{ route('reports.report.default', <a href="{{ route('reports.report.'~type,
['default', ['default',
'currentFiscalYearStart', 'currentFiscalYearStart',
'currentFiscalYearEnd', 'currentFiscalYearEnd',
@ -142,45 +145,7 @@
</li> </li>
{% endif %} {% endif %}
<li> <li>
<a href="{{ route('reports.report.default', <a href="{{ route('reports.report.'~type,
[
start.format('Ymd'),
'currentMonthEnd',
accountList
]) }}">{{ 'report_all_time_quick'|_ }}</a>
</li>
</ul>
<h4>{{ 'quick_link_audit_report'|_ }}</h4>
<ul>
<li>
<a href="{{ route('reports.report.audit',
[
'currentMonthStart',
'currentMonthEnd',
accountList
]) }}">{{ 'report_this_month_quick'|_ }}</a>
</li>
<li>
<a href="{{ route('reports.report.audit',
[
'currentYearStart',
'currentYearEnd',
accountList
]) }}">{{ 'report_this_year_quick'|_ }}</a>
</li>
{% if customFiscalYear == 1 %}
<li>
<a href="{{ route('reports.report.audit',
[
'currentFiscalYearStart',
'currentFiscalYearEnd',
accountList
]) }}">{{ 'report_this_fiscal_year_quick'|_ }}</a>
</li>
{% endif %}
<li>
<a href="{{ route('reports.report.audit',
[ [
start.format('Ymd'), start.format('Ymd'),
'currentMonthEnd', 'currentMonthEnd',
@ -188,6 +153,7 @@
]) }}">{{ 'report_all_time_quick'|_ }}</a> ]) }}">{{ 'report_all_time_quick'|_ }}</a>
</li> </li>
</ul> </ul>
{% endfor %}
<p> <p>
<em>{{ 'reports_can_bookmark'|_ }}</em> <em>{{ 'reports_can_bookmark'|_ }}</em>
</p> </p>