Small extension of category report.

This commit is contained in:
James Cole
2016-11-13 20:18:01 +01:00
parent 0c0f2109f6
commit 0bb07e1eeb
2 changed files with 197 additions and 55 deletions

View File

@@ -15,11 +15,13 @@ namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon; use Carbon\Carbon;
use Crypt;
use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Collector\JournalCollector;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
/** /**
* Class MonthReportGenerator * Class MonthReportGenerator
@@ -34,9 +36,22 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
private $categories; private $categories;
/** @var Carbon */ /** @var Carbon */
private $end; private $end;
/** @var Collection */
private $expenses;
/** @var Collection */
private $income;
/** @var Carbon */ /** @var Carbon */
private $start; private $start;
/**
* MonthReportGenerator constructor.
*/
public function __construct()
{
$this->income = new Collection;
$this->expenses = new Collection;
}
/** /**
* @return string * @return string
*/ */
@@ -47,9 +62,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
$reportType = 'category'; $reportType = 'category';
$accountSummary = $this->getAccountSummary(); $accountSummary = $this->getAccountSummary();
$categorySummary = $this->getCategorySummary(); $categorySummary = $this->getCategorySummary();
$averageExpenses = $this->getAverageExpenses();
// render! // render!
return view('reports.category.month', compact('accountIds', 'categoryIds', 'reportType', 'accountSummary', 'categorySummary')) return view('reports.category.month', compact('accountIds', 'categoryIds', 'reportType', 'accountSummary', 'categorySummary','averageExpenses'))
->with('start', $this->start)->with('end', $this->end) ->with('start', $this->start)->with('end', $this->end)
->with('categories', $this->categories) ->with('categories', $this->categories)
->with('accounts', $this->accounts) ->with('accounts', $this->accounts)
@@ -143,6 +159,49 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
} }
/**
* @return array
*/
private function getAverageExpenses(): array
{
$expenses = $this->getExpenses();
$result = [];
/** @var Transaction $transaction */
foreach ($expenses as $transaction) {
// opposing name and ID:
$opposingId = $transaction->opposing_account_id;
// is not set?
if (!isset($result[$opposingId])) {
$name = $transaction->opposing_account_name;
$encrypted = intval($transaction->opposing_account_encrypted);
$name = $encrypted === 1 ? Crypt::decrypt($name) : $name;
$result[$opposingId] = [
'name' => $name,
'count' => 1,
'id' => $opposingId,
'average' => $transaction->transaction_amount,
'sum' => $transaction->transaction_amount,
];
continue;
}
$result[$opposingId]['count']++;
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count']));
}
// sort result by average:
$average = [];
foreach ($result as $key => $row) {
$average[$key] = floatval($row['average']);
}
array_multisort($average, SORT_ASC, $result);
return $result;
}
/** /**
* @return array * @return array
*/ */
@@ -222,14 +281,21 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
*/ */
private function getExpenses(): Collection private function getExpenses(): Collection
{ {
if ($this->expenses->count() > 0) {
Log::debug('Return previous set of expenses.');
return $this->expenses;
}
$collector = new JournalCollector(auth()->user()); $collector = new JournalCollector(auth()->user());
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end) $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount()->disableFilter(); ->setCategories($this->categories)->withOpposingAccount()->disableFilter();
$accountIds = $this->accounts->pluck('id')->toArray(); $accountIds = $this->accounts->pluck('id')->toArray();
$transactions = $collector->getJournals(); $transactions = $collector->getJournals();
$transactions = self::filterExpenses($transactions, $accountIds); $transactions = self::filterExpenses($transactions, $accountIds);
$this->expenses = $transactions;
return $transactions; return $transactions;
} }
@@ -239,6 +305,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
*/ */
private function getIncome(): Collection private function getIncome(): Collection
{ {
if ($this->income->count() > 0) {
return $this->income;
}
$collector = new JournalCollector(auth()->user()); $collector = new JournalCollector(auth()->user());
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end) $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
@@ -246,6 +316,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
$accountIds = $this->accounts->pluck('id')->toArray(); $accountIds = $this->accounts->pluck('id')->toArray();
$transactions = $collector->getJournals(); $transactions = $collector->getJournals();
$transactions = self::filterIncome($transactions, $accountIds); $transactions = self::filterIncome($transactions, $accountIds);
$this->income = $transactions;
return $transactions; return $transactions;
} }

View File

@@ -89,73 +89,77 @@
</div> </div>
</div> </div>
{% if categories.count > 1 %} {% if categories.count > 1 %}
<div class="col-lg-2 col-md-3"> <div class="col-lg-2 col-md-3">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'income_per_category'|_ }}</h3> <h3 class="box-title">{{ 'income_per_category'|_ }}</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<canvas id="categories-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <canvas id="categories-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas>
<label style="font-weight:normal;"> <label style="font-weight:normal;">
<input type="checkbox" id="categories-in-pie-chart-checked"> <small>{{ 'include_not_in_category'|_ }}</small> <input type="checkbox" id="categories-in-pie-chart-checked">
</label> <small>{{ 'include_not_in_category'|_ }}</small>
</label>
</div>
</div> </div>
</div> </div>
</div> <div class="col-lg-2 col-md-3">
<div class="col-lg-2 col-md-3"> <div class="box">
<div class="box"> <div class="box-header with-border">
<div class="box-header with-border"> <h3 class="box-title">{{ 'expense_per_category'|_ }}</h3>
<h3 class="box-title">{{ 'expense_per_category'|_ }}</h3> </div>
</div> <div class="box-body">
<div class="box-body"> <canvas id="categories-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas>
<canvas id="categories-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;">
<label style="font-weight:normal;"> <input type="checkbox" id="categories-out-pie-chart-checked">
<input type="checkbox" id="categories-out-pie-chart-checked"> <small>{{ 'include_not_in_category'|_ }}</small> <small>{{ 'include_not_in_category'|_ }}</small>
</label> </label>
</div>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
{% if accounts.count > 1 %} {% if accounts.count > 1 %}
<div class="col-lg-2 col-md-3"> <div class="col-lg-2 col-md-3">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'income_per_account'|_ }}</h3> <h3 class="box-title">{{ 'income_per_account'|_ }}</h3>
</div> </div>
<div class="box-body"> <div class="box-body">
<canvas id="accounts-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <canvas id="accounts-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas>
<label style="font-weight:normal;"> <label style="font-weight:normal;">
<input type="checkbox" id="accounts-in-pie-chart-checked"> <small>{{ 'include_not_in_category'|_ }}</small> <input type="checkbox" id="accounts-in-pie-chart-checked">
</label> <small>{{ 'include_not_in_category'|_ }}</small>
</label>
</div>
</div> </div>
</div> </div>
</div> <div class="col-lg-2 col-md-3">
<div class="col-lg-2 col-md-3"> <div class="box">
<div class="box"> <div class="box-header with-border">
<div class="box-header with-border"> <h3 class="box-title">{{ 'expense_per_account'|_ }}</h3>
<h3 class="box-title">{{ 'expense_per_account'|_ }}</h3> </div>
</div> <div class="box-body">
<div class="box-body"> <canvas id="accounts-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas>
<canvas id="accounts-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;">
<label style="font-weight:normal;"> <input type="checkbox" id="accounts-out-pie-chart-checked">
<input type="checkbox" id="accounts-out-pie-chart-checked"> <small>{{ 'include_not_in_category'|_ }}</small> <small>{{ 'include_not_in_category'|_ }}</small>
</label> </label>
</div>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'income_and_expenses'|_ }}</h3> <h3 class="box-title">{{ 'income_and_expenses'|_ }}</h3>
</div>
<div class="box-body">
<canvas id="in-out-chart" style="margin:0 auto;" height="300"></canvas>
</div>
</div> </div>
<div class="box-body">
<canvas id="in-out-chart" style="margin:0 auto;" height="300"></canvas>
</div>
</div>
</div> </div>
</div> </div>
{# {#
@@ -164,6 +168,73 @@
In a bar chart, possibly grouped by expense/revenue account. In a bar chart, possibly grouped by expense/revenue account.
#} #}
<div class="row">
<div class="col-lg-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'average_spending_per_account'|_ }}</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover sortable">
<thead>
<tr>
<th>{{ 'account'|_ }}</th>
<th>{{ 'spent_average'|_ }}</th>
<th>{{ 'transaction_count'|_ }}</th>
</tr>
</thead>
<tbody>
{% for row in averageExpenses %}
<tr>
<td data-value="{{ row.name }}">
<a href="{{ route('accounts.show', row.id) }}">{{ row.name }}</a>
</td>
<td>
{{ row.average|formatAmount }}
</td>
<td>
{{ row.count }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: 10}) }})</h3>
</div>
<div class="box-body">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'average_income_per_account'|_ }}</h3>
</div>
<div class="box-body">
</div>
</div>
</div>
<div class="col-lg-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'income'|_ }} ({{ trans('firefly.topX', {number: 10}) }})</h3>
</div>
<div class="box-body">
</div>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
List of spending (withdrawals) by account, if relevant. Grouped:<br> List of spending (withdrawals) by account, if relevant. Grouped:<br>