Should simplify reports.

This commit is contained in:
James Cole 2015-05-16 14:51:23 +02:00
parent bca2ddd529
commit cdeac2c6db
15 changed files with 593 additions and 197 deletions

View File

@ -0,0 +1,53 @@
<?php
namespace FireflyIII\Helpers\Collection;
use Illuminate\Support\Collection;
/**
* Class Balance
*
* @package FireflyIII\Helpers\Collection
*/
class Balance
{
/** @var BalanceHeader */
protected $balanceHeader;
/** @var Collection */
protected $balanceLines;
/**
*
*/
public function __construct()
{
$this->balanceLines = new Collection;
}
/**
* @param BalanceLine $line
*/
public function addBalanceLine(BalanceLine $line)
{
$this->balanceLines->push($line);
}
/**
* @return BalanceHeader
*/
public function getBalanceHeader()
{
return $this->balanceHeader;
}
/**
* @param BalanceHeader $balanceHeader
*/
public function setBalanceHeader($balanceHeader)
{
$this->balanceHeader = $balanceHeader;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
/**
* Class BalanceEntry
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceEntry
{
/** @var AccountModel */
protected $account;
/** @var float */
protected $spent = 0.0;
/**
* @return AccountModel
*/
public function getAccount()
{
return $this->account;
}
/**
* @param AccountModel $account
*/
public function setAccount($account)
{
$this->account = $account;
}
/**
* @return float
*/
public function getSpent()
{
return $this->spent;
}
/**
* @param float $spent
*/
public function setSpent($spent)
{
$this->spent = $spent;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Account as AccountModel;
use Illuminate\Support\Collection;
/**
* Class BalanceHeader
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceHeader
{
/** @var Collection */
protected $accounts;
/**
*
*/
public function __construct()
{
$this->accounts = new Collection;
}
/**
* @param AccountModel $account
*/
public function addAccount(AccountModel $account)
{
$this->accounts->push($account);
}
/**
* @return Collection
*/
public function getAccounts()
{
return $this->accounts;
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Budget as BudgetModel;
use Illuminate\Support\Collection;
/**
* Class BalanceLine
*
* @package FireflyIII\Helpers\Collection
*/
class BalanceLine
{
/** @var Collection */
protected $balanceEntries;
/** @var BudgetModel */
protected $budget;
/**
*
*/
public function __construct()
{
$this->balanceEntries = new Collection;
}
public function addBalanceEntry(BalanceEntry $balanceEntry)
{
$this->balanceEntries->push($balanceEntry);
}
/**
* @return Collection
*/
public function getBalanceEntries()
{
return $this->balanceEntries;
}
/**
* @param Collection $balanceEntries
*/
public function setBalanceEntries($balanceEntries)
{
$this->balanceEntries = $balanceEntries;
}
/**
* @return BudgetModel
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param BudgetModel $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
}
}

View File

@ -8,7 +8,71 @@
namespace FireflyIII\Helpers\Collection;
use FireflyIII\Models\Category as CategoryModel;
use Illuminate\Support\Collection;
/**
* Class Category
*
* @package FireflyIII\Helpers\Collection
*/
class Category
{
/** @var Collection */
protected $categories;
/** @var float */
protected $total = 0;
/**
*
*/
public function __construct()
{
$this->categories = new Collection;
}
/**
* @param CategoryModel $category
*/
public function addCategory(CategoryModel $category)
{
if ($category->spent > 0) {
$this->categories->push($category);
}
}
/**
* @param float $add
*/
public function addTotal($add)
{
$this->total += floatval($add);
}
/**
* @return Collection
*/
public function getCategories()
{
$this->categories->sortByDesc(
function (CategoryModel $category) {
return $category->spent;
}
);
return $this->categories;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
class Category {
}

View File

@ -5,12 +5,17 @@ namespace FireflyIII\Helpers\Report;
use App;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account as AccountCollection;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\BalanceEntry;
use FireflyIII\Helpers\Collection\BalanceHeader;
use FireflyIII\Helpers\Collection\BalanceLine;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\BudgetLine;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget as BudgetModel;
use FireflyIII\Models\LimitRepetition;
/**
@ -69,6 +74,61 @@ class ReportHelper implements ReportHelperInterface
return $object;
}
/**
*
* The balance report contains a Balance object which in turn contains:
*
* A BalanceHeader object which contains all relevant user asset accounts for the report.
*
* A number of BalanceLine objects, which hold:
* - A budget
* - A number of BalanceEntry objects.
*
* The BalanceEntry object holds:
* - The same budget (again)
* - A user asset account as mentioned in the BalanceHeader
* - The amount of money spent on the budget by the user asset account
*
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared)
{
$repository = App::make('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$balance = new Balance;
// build a balance header:
$header = new BalanceHeader;
$accounts = $this->query->getAllAccounts($start, $end, $shared);
$budgets = $repository->getBudgets();
foreach ($accounts as $account) {
$header->addAccount($account);
}
foreach ($budgets as $budget) {
$line = new BalanceLine;
$line->setBudget($budget);
// loop accounts:
foreach ($accounts as $account) {
$balanceEntry = new BalanceEntry;
$balanceEntry->setAccount($account);
$balanceEntry->setSpent(rand(1, 100));
$line->addBalanceEntry($balanceEntry);
}
// add line to balance:
$balance->addBalanceLine($line);
}
$balance->setBalanceHeader($header);
return $balance;
}
/**
* @param Carbon $start
* @param Carbon $end
@ -143,7 +203,23 @@ class ReportHelper implements ReportHelperInterface
*/
public function getCategoryReport(Carbon $start, Carbon $end, $shared)
{
return null;
$object = new CategoryCollection;
/**
* GET CATEGORIES:
*/
/** @var \FireflyIII\Repositories\Category\CategoryRepositoryInterface $repository */
$repository = App::make('FireflyIII\Repositories\Category\CategoryRepositoryInterface');
$set = $repository->getCategories();
foreach ($set as $category) {
$spent = $repository->spentInPeriod($category, $start, $end, $shared);
$category->spent = $spent;
$object->addCategory($category);
$object->addTotal($spent);
}
return $object;
}
/**

View File

@ -4,10 +4,11 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Account;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
use FireflyIII\Helpers\Collection\Balance;
use FireflyIII\Helpers\Collection\Budget as BudgetCollection;
use FireflyIII\Helpers\Collection\Category as CategoryCollection;
use FireflyIII\Helpers\Collection\Expense;
use FireflyIII\Helpers\Collection\Income;
/**
* Interface ReportHelperInterface
@ -29,6 +30,15 @@ interface ReportHelperInterface
*/
public function getAccountReport(Carbon $date, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end
* @param boolean $shared
*
* @return Balance
*/
public function getBalanceReport(Carbon $start, Carbon $end, $shared);
/**
* @param Carbon $start
* @param Carbon $end

View File

@ -86,6 +86,70 @@ class ReportQuery implements ReportQueryInterface
return floatval($this->balancedTransactionsList($account, $start, $end)->sum('queryAmount'));
}
/**
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = false)
{
$query = $this->queryJournalsWithTransactions($start, $end);
if ($includeShared === false) {
// only get withdrawals not from a shared account
// and transfers from a shared account.
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any withdrawal is fine.
$query->where('transaction_types.type', 'Withdrawal');
}
$query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date');
// get everything, decrypt and return
$data = $query->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
DB::Raw('SUM(`t_from`.`amount`) as `queryAmount`'),
'transaction_journals.date',
't_to.account_id as account_id',
'ac_to.name as name',
'ac_to.encrypted as account_encrypted'
]
);
$data->each(
function (Model $object) {
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
$data->sortByDesc('queryAmount');
return $data;
}
/**
* Get a users accounts combined with various meta-data related to the start and end date.
*
@ -232,69 +296,6 @@ class ReportQuery implements ReportQueryInterface
return $data;
}
/**
* This method returns all "expense" journals in a certain period, which are both transfers to a shared account
* and "ordinary" withdrawals. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
* @param bool $includeShared
*
* @return Collection
*
*/
public function expenseInPeriod(Carbon $start, Carbon $end, $includeShared = false) {
$query = $this->queryJournalsWithTransactions($start, $end);
if ($includeShared === false) {
// only get withdrawals not from a shared account
// and transfers from a shared account.
$query->where(
function (Builder $query) {
$query->where(
function (Builder $q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedAsset"');
}
);
$query->orWhere(
function (Builder $q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedAsset"');
}
);
}
);
} else {
// any withdrawal is fine.
$query->where('transaction_types.type', 'Withdrawal');
}
$query->groupBy('transaction_journals.id')->orderBy('transaction_journals.date');
// get everything, decrypt and return
$data = $query->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
DB::Raw('SUM(`t_from`.`amount`) as `queryAmount`'),
'transaction_journals.date',
't_to.account_id as account_id',
'ac_to.name as name',
'ac_to.encrypted as account_encrypted'
]
);
$data->each(
function (Model $object) {
$object->name = intval($object->account_encrypted) == 1 ? Crypt::decrypt($object->name) : $object->name;
}
);
$data->sortByDesc('queryAmount');
return $data;
}
/**
* Gets a list of expenses grouped by the budget they were filed under.
*

View File

@ -123,6 +123,7 @@ interface ReportQueryInterface
*/
public function journalsByCategory(Carbon $start, Carbon $end, $includeShared = false);
/**
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.

View File

@ -160,6 +160,7 @@ class ReportController extends Controller
$expenses = $this->helper->getExpenseReport($start, $end, $shared);
$budgets = $this->helper->getBudgetReport($start, $end, $shared);
$categories = $this->helper->getCategoryReport($start, $end, $shared);
$balance = $this->helper->getBalanceReport($start, $end, $shared);
// /**
// * DO BUDGETS.
@ -234,7 +235,7 @@ class ReportController extends Controller
'accounts',
'incomes', 'incomeTopLength',
'expenses', 'expenseTopLength',
'budgets',
'budgets','balance',
'categories'
)
);

View File

@ -103,6 +103,7 @@ return [
'leftUnbalanced' => 'Left unbalanced',
'expectedBalance' => 'Expected balance',
'outsideOfBudgets' => 'Outside of budgets',
'leftInBudget' => 'Left in budget',
'difference' => 'Difference',
'in' => 'In',

View File

@ -103,6 +103,7 @@ return [
'leftUnbalanced' => 'Ongecorrigeerd',
'expectedBalance' => 'Verwacht saldo',
'outsideOfBudgets' => 'Buiten budgetten',
'leftInBudget' => 'Over van budget',
'difference' => 'Verschil',
'in' => 'In',

View File

@ -0,0 +1,122 @@
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-sort-amount-asc fa-fw"></i>
{{ 'budgets'|_ }} ({{ 'splitByAccount'|_|lower }})
</div>
<table class="table table-bordered table-striped">
<!-- build balance report header -->
<tr>
<th colspan="2">{{ 'budgets'|_ }}</th>
{% for account in balance.getBalanceHeader.getAccounts %}
<th><a href="{{route('accounts.show',account.id)}}">{{ account.name }}</a></th>
{% endfor %}
<th colspan="2">
{{ 'leftInBudget'|_ }}
</th>
</tr>
</table>
<!--
<table class="table table-bordered table-striped">
<tr>
<th colspan="2">{{ 'budgets'|_ }}</th>
{% for account in accounts %}
<th><a href="{{route('accounts.show',account.id)}}">{{ account.name }}</a></th>
{% endfor %}
<th colspan="2">
{{ 'leftInBudget'|_ }}
</th>
</tr>
{% for data in budgets %}
{% if data[0] %}
<tr>
<td>{{ data[0].name }}</td>
<td>
{% if data[1] %}
{{ data[1].amount|formatAmount }}
{% else %}
{{ 0|formatAmount }}
{% endif %}
</td>
{% set spent = 0 %}
{% for account in accounts %}
{% if account.budgetInformation[id] %}
<td>
{% if id == 0 %}
<a href="{{route('reports.no-budget',[account, year, month])}}" class="openModal">
{{ account.budgetInformation[id].queryAmount|formatAmount }}
</a>
{% else %}
{{ account.budgetInformation[id].queryAmount|formatAmount }}
{% endif %}
</td>
{% set spent = spent + account.budgetInformation[id].queryAmount %}
{% else %}
<td>{{ 0|formatAmount }}</td>
{% endif %}
{% endfor %}
<td>
{% if data[1] %}
{{ (data[1].amount - data[3])|formatAmount }}
{% else %}
{{ (0 - data[3])|formatAmount }}
{% endif %}
</td>
<td>{{ data[2]|formatAmount }}</td>
</tr>
{% endif %}
{% endfor %}
<tr>
<td colspan="2">{{ 'balancedByTransfersAndTags'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
<td>
<a href="{{route('reports.balanced-transfers',[account, year, month])}}" class="openModal">{{ account.balancedAmount|formatAmount }}</a>
</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">{{ 'leftUnbalanced'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
{% if account.budgetInformation[0] %}
<td>
{% if account.budgetInformation[0].queryAmount + account.balancedAmount != 0.0 %}
<a href="{{route('reports.left-unbalanced',[account, year, month])}}" class="openModal">{{ (account.budgetInformation[0].queryAmount + account.balancedAmount)|formatAmount }}</a>
{% else %}
{{ (account.budgetInformation[0].queryAmount + account.balancedAmount)|formatAmount }}
{% endif %}
</td>
{% else %}
<td>{{ 0|formatAmount }}</td>
{% endif %}
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2"><em>{{ 'sum'|_ }}</em></td>
{% for account in accounts %}
{% if not account.hide %}
<td>{{ accountAmounts[account.id]|formatAmount }}</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">{{ 'expectedBalance'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
<td>{{ (account.startBalance + accountAmounts[account.id])|formatAmount }}</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
</table>
-->
</div>

View File

@ -0,0 +1,24 @@
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bar-chart fa-fw"></i>
{{ 'categories'|_ }}
</div>
<table class="table table-bordered">
<tr>
<th>{{ 'categories'|_ }}</th>
<th>{{ 'spent'|_ }}</th>
</tr>
{% for cat in categories.getCategories %}
<tr>
<td>
<a href="{{ route('categories.show',cat.id) }}">{{ cat.name }}</a>
</td>
<td><span class="text-danger">{{ (cat.spent)|formatAmountPlain }}</span></td>
</tr>
{% endfor %}
<tr>
<td><em>{{ 'sum'|_ }}</em></td>
<td class="text-danger">{{ categories.getTotal|formatAmountPlain }}</td>
</tr>
</table>
</div>

View File

@ -36,29 +36,8 @@
{% include 'partials/reports/budgets.twig' %}
</div>
<div class="col-lg-4 col-md-4 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-bar-chart fa-fw"></i>
{{ 'categories'|_ }}
</div>
<table class="table table-bordered">
<tr>
<th>{{ 'categories'|_ }}</th>
<th>{{ 'spent'|_ }}</th>
</tr>
{% set sum = 0 %}
{% for data in categories %}
{% if data[1] > 0 %}
<tr>
<td>
<a href="{{route('categories.show',data[0].id)}}">{{ data[0].name }}</a>
</td>
<td><span class="text-danger">{{ (data[1])|formatAmountPlain }}</span></td>
</tr>
{% endif %}
{% endfor %}
</table>
</div>
<!-- categories -->
{% include 'partials/reports/categories.twig' %}
</div>
</div>
<div class="row">
@ -68,112 +47,7 @@
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-sort-amount-asc fa-fw"></i>
{{ 'budgets'|_ }} ({{ 'splitByAccount'|_|lower }})
</div>
<table class="table table-bordered table-striped">
<tr>
<th colspan="2">{{ 'budgets'|_ }}</th>
{% for account in accounts %}
<th><a href="{{route('accounts.show',account.id)}}">{{ account.name }}</a></th>
{% endfor %}
<th colspan="2">
{{ 'leftInBudget'|_ }}
</th>
</tr>
{% for data in budgets %}
{% if data[0] %}
<tr>
<td>{{ data[0].name }}</td>
<td>
{% if data[1] %}
{{ data[1].amount|formatAmount }}
{% else %}
{{ 0|formatAmount }}
{% endif %}
</td>
{% set spent = 0 %}
{% for account in accounts %}
{% if account.budgetInformation[id] %}
<td>
{% if id == 0 %}
<a href="{{route('reports.no-budget',[account, year, month])}}" class="openModal">
{{ account.budgetInformation[id].queryAmount|formatAmount }}
</a>
{% else %}
{{ account.budgetInformation[id].queryAmount|formatAmount }}
{% endif %}
</td>
{% set spent = spent + account.budgetInformation[id].queryAmount %}
{% else %}
<td>{{ 0|formatAmount }}</td>
{% endif %}
{% endfor %}
<td>
{% if data[1] %}
{{ (data[1].amount - data[3])|formatAmount }}
{% else %}
{{ (0 - data[3])|formatAmount }}
{% endif %}
</td>
<td>{{ data[2]|formatAmount }}</td>
</tr>
{% endif %}
{% endfor %}
<tr>
<td colspan="2">{{ 'balancedByTransfersAndTags'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
<td>
<a href="{{route('reports.balanced-transfers',[account, year, month])}}" class="openModal">{{ account.balancedAmount|formatAmount }}</a>
</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">{{ 'leftUnbalanced'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
{% if account.budgetInformation[0] %}
<td>
{% if account.budgetInformation[0].queryAmount + account.balancedAmount != 0.0 %}
<a href="{{route('reports.left-unbalanced',[account, year, month])}}" class="openModal">{{ (account.budgetInformation[0].queryAmount + account.balancedAmount)|formatAmount }}</a>
{% else %}
{{ (account.budgetInformation[0].queryAmount + account.balancedAmount)|formatAmount }}
{% endif %}
</td>
{% else %}
<td>{{ 0|formatAmount }}</td>
{% endif %}
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2"><em>{{ 'sum'|_ }}</em></td>
{% for account in accounts %}
{% if not account.hide %}
<td>{{ accountAmounts[account.id]|formatAmount }}</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">{{ 'expectedBalance'|_ }}</td>
{% for account in accounts %}
{% if not account.hide %}
<td>{{ (account.startBalance + accountAmounts[account.id])|formatAmount }}</td>
{% endif %}
{% endfor %}
<td colspan="2">&nbsp;</td>
</tr>
</table>
</div>
{% include 'partials/reports/budgets-vs-accounts.twig' %}
</div>
</div>
<div class="row">