mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-15 08:35:00 +00:00
Small extension of category report.
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
|
Reference in New Issue
Block a user