This commit is contained in:
James Cole
2015-02-23 20:25:48 +01:00
parent 5a0a28a04c
commit 220d689f69
15 changed files with 1861 additions and 10 deletions

View File

@@ -0,0 +1,176 @@
<?php
namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class ReportHelper
*
* @package FireflyIII\Helpers\Report
*/
class ReportHelper implements ReportHelperInterface
{
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15)
{
$result = $this->_queries->journalsByExpenseAccount($start, $end);
$array = $this->_helper->makeArray($result);
$limited = $this->_helper->limitArray($array, $limit);
return $limited;
}
/**
* @return Carbon
*/
public function firstDate()
{
$journal = Auth::user()->transactionjournals()->orderBy('date', 'ASC')->first();
if ($journal) {
return $journal->date;
}
return Carbon::now();
}
/**
* This method gets some kind of list for a monthly overview.
*
* @param Carbon $date
*
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date)
{
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
// all budgets
$set = \Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = $this->_helper->makeArray($set);
$amountSet = $this->_queries->journalsByBudget($start, $end);
$amounts = $this->_helper->makeArray($amountSet);
$combined = $this->_helper->mergeArrays($budgets, $amounts);
$combined[0]['spent'] = isset($combined[0]['spent']) ? $combined[0]['spent'] : 0.0;
$combined[0]['amount'] = isset($combined[0]['amount']) ? $combined[0]['amount'] : 0.0;
$combined[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $this->_queries->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$combined[0]['spent'] += floatval($transfer->amount) * -1;
}
return $combined;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$months = [];
while ($start <= $end) {
$months[] = [
'formatted' => $start->format('F Y'),
'month' => intval($start->format('m')),
'year' => intval($start->format('Y')),
];
$start->addMonth();
}
return $months;
}
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date)
{
$start = clone $date;
$end = Carbon::now();
$years = [];
while ($start <= $end) {
$years[] = $start->format('Y');
$start->addYear();
}
return $years;
}
/**
* @param Carbon $date
*
* @return array
*/
public function yearBalanceReport(Carbon $date)
{
$start = clone $date;
$end = clone $date;
$sharedAccounts = [];
$sharedCollection = \Auth::user()->accounts()
->leftJoin('account_meta', 'account_meta.account_id', '=', 'accounts.id')
->where('account_meta.name', '=', 'accountRole')
->where('account_meta.data', '=', json_encode('sharedExpense'))
->get(['accounts.id']);
foreach ($sharedCollection as $account) {
$sharedAccounts[] = $account->id;
}
$accounts = \Auth::user()->accounts()->accountTypeIn(['Default account', 'Asset account'])->get()->filter(
function (Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;
}
return null;
}
);
$report = [];
$start->startOfYear()->subDay();
$end->endOfYear();
foreach ($accounts as $account) {
$report[] = [
'start' => \Steam::balance($account, $start),
'end' => \Steam::balance($account, $end),
'account' => $account,
'shared' => $account->accountRole == 'sharedExpense'
];
}
return $report;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface ReportHelperInterface
*
* @package FireflyIII\Helpers\Report
*/
interface ReportHelperInterface
{
/**
* @return Carbon
*/
public function firstDate();
/**
* @param Carbon $date
*
* @return array
*/
public function listOfMonths(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function listOfYears(Carbon $date);
/**
* @param Carbon $date
*
* @return array
*/
public function yearBalanceReport(Carbon $date);
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15);
/**
* This method gets some kind of list for a monthly overview.
*
* @param Carbon $date
*
* @return Collection
*/
public function getBudgetsForMonth(Carbon $date);
}

View File

@@ -0,0 +1,389 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 22/02/15
* Time: 18:30
*/
namespace FireflyIII\Helpers\Report;
use Auth;
use Carbon\Carbon;
use DB;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class ReportQuery
*
* @package FireflyIII\Helpers\Report
*/
class ReportQuery implements ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @return Collection
*/
public function accountList()
{
return \Auth::user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', "accountRole");
}
)
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
->where('active', 1)
->where(
function (Builder $query) {
$query->where('account_meta.data', '!=', '"sharedExpense"');
$query->orWhereNull('account_meta.data');
}
)
->get(['accounts.*']);
}
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date')
->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_journals.encrypted',
'transaction_types.type',
't_to.amount', 'transaction_journals.date', 't_from.account_id as account_id',
'ac_from.name as name']
);
}
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end)
{
return \Auth::user()->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('budgets.id')
->orderBy('budgets.name', 'ASC')
->get(['budgets.id', 'budgets.name', \DB::Raw('SUM(`transactions`.`amount`) AS `spent`')]);
}
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end)
{
return \Auth::user()->transactionjournals()
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', '=', 'transaction_types.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->where('account_meta.data', '!=', '"sharedExpense"')
->where('transaction_types.type', 'Withdrawal')
->groupBy('categories.id')
->orderBy('amount')
->get(['categories.id', 'categories.name', \DB::Raw('SUM(`transactions`.`amount`) AS `amount`')]);
}
/**
* 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.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)
->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_to.account_id')
->orderBy('amount', 'DESC')
->get(['t_to.account_id as id', 'ac_to.name as name', DB::Raw('SUM(t_to.amount) as `amount`')]);
}
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount')
->get(['t_from.account_id as account_id', 'ac_from.name as name', DB::Raw('SUM(t_from.amount) as `amount`')]);
}
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->where('account_meta.data', '"sharedExpense"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', \Auth::user()->id)
->get(
['transaction_journals.id', 'transaction_journals.description', 'transactions.account_id', 'accounts.name',
'transactions.amount']
);
}
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end)
{
return TransactionJournal::
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where(
'transactions.amount', '>', 0
);
}
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->leftJoin(
'category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'
)
->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id')
->where('account_meta.data', '"sharedExpense"')
->after($start)
->before($end)
->where('transaction_types.type', 'Transfer')
->where('transaction_journals.user_id', \Auth::user()->id)
->groupBy('categories.name')
->get(
[
'categories.id',
'categories.name as name',
\DB::Raw('SUM(`transactions`.`amount`) * -1 AS `amount`')
]
);
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* Created by PhpStorm.
* User: sander
* Date: 22/02/15
* Time: 18:30
*/
namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Interface ReportQueryInterface
*
* @package FireflyIII\Helpers\Report
*/
interface ReportQueryInterface
{
/**
* This query retrieves a list of accounts that are active and not shared.
*
* @return Collection
*/
public function accountList();
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end);
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByBudget(Carbon $start, Carbon $end);
/**
* Gets a list of categories and the expenses therein, grouped by the relevant category.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByCategory(Carbon $start, Carbon $end);
/**
* 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.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByExpenseAccount(Carbon $start, Carbon $end);
/**
* This method returns all deposits into asset accounts, grouped by the revenue account,
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpenses(Carbon $start, Carbon $end);
/**
* With a slightly misleading name, this query returns all transfers to shared accounts
* which are technically expenses, since it won't be just your money that gets spend.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function sharedExpensesByCategory(Carbon $start, Carbon $end);
}

View File

@@ -4,12 +4,15 @@ use App;
use Auth;
use Carbon\Carbon;
use Crypt;
use Exception;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use Grumpydictator\Gchart\GChart;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\JoinClause;
@@ -370,5 +373,152 @@ class GoogleChartController extends Controller
}
/**
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExp($year, GChart $chart, ReportQueryInterface $query)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Month', 'date');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
// get report query interface.
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$income = $query->incomeByPeriod($start, $currentEnd);
$incomeSum = 0;
foreach ($income as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expense = $query->journalsByExpenseAccount($start, $currentEnd);
$expenseSum = 0;
foreach ($expense as $entry) {
$expenseSum += floatval($entry->amount);
}
$chart->addRow(clone $start, $incomeSum, $expenseSum);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
/**
*
* @param $year
*
* @return \Illuminate\Http\JsonResponse
*/
public function yearInExpSum($year, GChart $chart, ReportQueryInterface $query)
{
try {
$start = new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$chart->addColumn('Summary', 'string');
$chart->addColumn('Income', 'number');
$chart->addColumn('Expenses', 'number');
$income = 0;
$expense = 0;
$count = 0;
$end = clone $start;
$end->endOfYear();
while ($start < $end) {
$currentEnd = clone $start;
$currentEnd->endOfMonth();
// total income:
$incomeResult = $query->incomeByPeriod($start, $currentEnd);
$incomeSum = 0;
foreach ($incomeResult as $entry) {
$incomeSum += floatval($entry->amount);
}
// total expenses:
$expenseResult = $query->journalsByExpenseAccount($start, $currentEnd);
$expenseSum = 0;
foreach ($expenseResult as $entry) {
$expenseSum += floatval($entry->amount);
}
$income += $incomeSum;
$expense += $expenseSum;
$count++;
$start->addMonth();
}
$chart->addRow('Sum', $income, $expense);
$count = $count > 0 ? $count : 1;
$chart->addRow('Average', ($income / $count), ($expense / $count));
$chart->generate();
return Response::json($chart->getData());
}
/**
* @param int $year
*
* @return $this|\Illuminate\Http\JsonResponse
*/
public function allBudgetsAndSpending($year, GChart $chart, BudgetRepositoryInterface $repository)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return view('error')->with('message', 'Invalid year.');
}
$budgets = Auth::user()->budgets()->get();
$budgets->sortBy('name');
$chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $repository->spentInMonth($budget, $start);
$row[] = $spent;
}
$chart->addRowArray($row);
$start->addMonth();
}
$chart->generate();
return Response::json($chart->getData());
}
}

View File

@@ -0,0 +1,206 @@
<?php namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use Exception;
use FireflyIII\Helpers\Report\ReportHelperInterface;
use FireflyIII\Helpers\Report\ReportQueryInterface;
use FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use Illuminate\Database\Query\JoinClause;
use Steam;
use View;
/**
* Class ReportController
*
* @package FireflyIII\Http\Controllers
*/
class ReportController extends Controller
{
/**
*
*/
public function __construct()
{
View::share('title', 'Reports');
View::share('mainTitleIcon', 'fa-line-chart');
}
/**
* @param ReportHelperInterface $helper
*
* @return View
*/
public function index(ReportHelperInterface $helper)
{
$start = $helper->firstDate();
$months = $helper->listOfMonths($start);
$years = $helper->listOfYears($start);
$title = 'Reports';
$mainTitleIcon = 'fa-line-chart';
return view('reports.index', compact('years', 'months', 'title', 'mainTitleIcon'));
}
/**
* @param string $year
* @param string $month
*
* @return \Illuminate\View\View
*/
public function month($year = '2014', $month = '1', ReportQueryInterface $query)
{
try {
new Carbon($year . '-' . $month . '-01');
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid date.');
}
$date = new Carbon($year . '-' . $month . '-01');
$subTitle = 'Report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$displaySum = true; // to show sums in report.
/**
*
* get income for month (date)
*
*/
$start = clone $date;
$start->startOfMonth();
$end = clone $date;
$end->endOfMonth();
/**
* Start getIncomeForMonth DONE
*/
$income = $query->incomeByPeriod($start, $end);
/**
* End getIncomeForMonth DONE
*/
/**
* Start getExpenseGroupedForMonth DONE
*/
$set = $query->journalsByExpenseAccount($start, $end);
$expenses = Steam::makeArray($set);
$expenses = Steam::sortArray($expenses);
$expenses = Steam::limitArray($expenses, 10);
/**
* End getExpenseGroupedForMonth DONE
*/
/**
* Start getBudgetsForMonth DONE
*/
$set = Auth::user()->budgets()
->leftJoin(
'budget_limits', function (JoinClause $join) use ($date) {
$join->on('budget_limits.budget_id', '=', 'budgets.id')->where('budget_limits.startdate', '=', $date->format('Y-m-d'));
}
)
->get(['budgets.*', 'budget_limits.amount as amount']);
$budgets = Steam::makeArray($set);
$amountSet = $query->journalsByBudget($start, $end);
$amounts = Steam::makeArray($amountSet);
$budgets = Steam::mergeArrays($budgets, $amounts);
$budgets[0]['spent'] = isset($budgets[0]['spent']) ? $budgets[0]['spent'] : 0.0;
$budgets[0]['amount'] = isset($budgets[0]['amount']) ? $budgets[0]['amount'] : 0.0;
$budgets[0]['name'] = 'No budget';
// find transactions to shared expense accounts, which are without a budget by default:
$transfers = $query->sharedExpenses($start, $end);
foreach ($transfers as $transfer) {
$budgets[0]['spent'] += floatval($transfer->amount) * -1;
}
/**
* End getBudgetsForMonth DONE
*/
/**
* Start getCategoriesForMonth DONE
*/
// all categories.
$result = $query->journalsByCategory($start, $end);
$categories = Steam::makeArray($result);
// all transfers
$result = $query->sharedExpensesByCategory($start, $end);
$transfers = Steam::makeArray($result);
$merged = Steam::mergeArrays($categories, $transfers);
// sort.
$sorted = Steam::sortNegativeArray($merged);
// limit to $limit:
$categories = Steam::limitArray($sorted, 10);
/**
* End getCategoriesForMonth DONE
*/
/**
* Start getAccountsForMonth
*/
$list = $query->accountList();
$accounts = [];
/** @var Account $account */
foreach ($list as $account) {
$id = intval($account->id);
/** @noinspection PhpParamsInspection */
$accounts[$id] = [
'name' => $account->name,
'startBalance' => Steam::balance($account, $start),
'endBalance' => Steam::balance($account, $end)
];
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
}
/**
* End getAccountsForMonth
*/
return View::make(
'reports.month',
compact(
'income', 'expenses', 'budgets', 'accounts', 'categories',
'date', 'subTitle', 'displaySum', 'subTitleIcon'
)
);
}
/**
* @param $year
*
* @return $this
*/
public function year($year, ReportHelperInterface $helper, ReportQueryInterface $query)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid date.');
}
$date = new Carbon('01-01-' . $year);
$end = clone $date;
$end->endOfYear();
$title = 'Reports';
$subTitle = $year;
$subTitleIcon = 'fa-bar-chart';
$mainTitleIcon = 'fa-line-chart';
$balances = $helper->yearBalanceReport($date);
$groupedIncomes = $query->journalsByRevenueAccount($date, $end);
$groupedExpenses = $query->journalsByExpenseAccount($date, $end);
//$groupedExpenses = $helper-> expensesGroupedByAccount($date, $end, 15);
return View::make(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
);
}
}

View File

@@ -149,9 +149,11 @@ Route::group(
Route::get('/chart/account/{account}/{view?}', ['uses' => 'GoogleChartController@accountBalanceChart']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
//Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
//Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
//Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
@@ -191,9 +193,9 @@ Route::group(
* Report Controller
*/
Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']);
//Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']);
//Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
//Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
Route::get('/reports/{year}', ['uses' => 'ReportController@year', 'as' => 'reports.year']);
Route::get('/reports/{year}/{month}', ['uses' => 'ReportController@month', 'as' => 'reports.month']);
Route::get('/reports/budget/{year}/{month}', ['uses' => 'ReportController@budget', 'as' => 'reports.budget']);
/**
* Search Controller

View File

@@ -56,12 +56,14 @@ class FireflyServiceProvider extends ServiceProvider
}
);
// preferences
$this->app->bind('FireflyIII\Repositories\Account\AccountRepositoryInterface', 'FireflyIII\Repositories\Account\AccountRepository');
$this->app->bind('FireflyIII\Repositories\Budget\BudgetRepositoryInterface', 'FireflyIII\Repositories\Budget\BudgetRepository');
$this->app->bind('FireflyIII\Repositories\Category\CategoryRepositoryInterface', 'FireflyIII\Repositories\Category\CategoryRepository');
$this->app->bind('FireflyIII\Repositories\Journal\JournalRepositoryInterface', 'FireflyIII\Repositories\Journal\JournalRepository');
$this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper');
$this->app->bind('FireflyIII\Helpers\Report\ReportQueryInterface', 'FireflyIII\Helpers\Report\ReportQuery');
}
}

View File

@@ -4,6 +4,7 @@ namespace FireflyIII\Support;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
/**
* Class Steam
@@ -31,4 +32,133 @@ class Steam
return $balance;
}
/**
* Turns a collection into an array. Needs the field 'id' for the key,
* and saves only 'name' and 'amount' as a sub array.
*
* @param Collection $collection
*
* @return array
*/
public function makeArray(Collection $collection)
{
$array = [];
foreach ($collection as $entry) {
$entry->spent = isset($entry->spent) ? floatval($entry->spent) : 0.0;
$id = intval($entry->id);
if (isset($array[$id])) {
$array[$id]['amount'] += floatval($entry->amount);
$array[$id]['spent'] += floatval($entry->spent);
} else {
$array[$id] = [
'amount' => floatval($entry->amount),
'spent' => floatval($entry->spent),
'name' => $entry->name
];
}
}
return $array;
}
/**
* Merges two of the arrays as defined above. Can't handle more (yet)
*
* @param array $one
* @param array $two
*
* @return array
*/
public function mergeArrays(array $one, array $two)
{
foreach ($two as $id => $value) {
// $otherId also exists in $one:
if (isset($one[$id])) {
$one[$id]['amount'] += $value['amount'];
$one[$id]['spent'] += $value['spent'];
} else {
$one[$id] = $value;
}
}
return $one;
}
/**
* Sort an array where all 'amount' keys are positive floats.
*
* @param array $array
*
* @return array
*/
public function sortArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['amount'] == $right['amount']) {
return 0;
}
return ($left['amount'] < $right['amount']) ? 1 : -1;
}
);
return $array;
}
/**
* Only return the top X entries, group the rest by amount
* and described as 'Others'. id = 0 as well
*
* @param array $array
* @param int $limit
*
* @return array
*/
public function limitArray(array $array, $limit = 10)
{
$others = [
'name' => 'Others',
'amount' => 0
];
$return = [];
$count = 0;
foreach ($array as $id => $entry) {
if ($count < ($limit - 1)) {
$return[$id] = $entry;
} else {
$others['amount'] += $entry['amount'];
}
$count++;
}
$return[0] = $others;
return $return;
}
/**
* Sort an array where all 'amount' keys are negative floats.
*
* @param array $array
*
* @return array
*/
public function sortNegativeArray(array $array)
{
uasort(
$array, function ($left, $right) {
if ($left['amount'] == $right['amount']) {
return 0;
}
return ($left['amount'] < $right['amount']) ? -1 : 1;
}
);
return $array;
}
}

View File

@@ -1,12 +1,12 @@
<?php
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\PiggyBankRepetition;
@@ -137,6 +137,14 @@ class TestDataSeeder extends Seeder
$acc_b = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Savings account', 'active' => 1]);
$acc_c = Account::create(['user_id' => $user->id, 'account_type_id' => $assetType->id, 'name' => 'Delete me', 'active' => 1]);
// create account meta:
$meta_a = AccountMeta::create(['account_id' => $acc_a->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
$meta_b = AccountMeta::create(['account_id' => $acc_b->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
$meta_c = AccountMeta::create(['account_id' => $acc_c->id, 'name' => 'accountRole', 'data' => 'defaultExpense']);
// var_dump($meta_a->toArray());
// var_dump($meta_b->toArray());
// var_dump($meta_c->toArray());
$acc_d = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Checking account initial balance', 'active' => 0]);
$acc_e = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Savings account initial balance', 'active' => 0]);
$acc_f = Account::create(['user_id' => $user->id, 'account_type_id' => $ibType->id, 'name' => 'Delete me initial balance', 'active' => 0]);
@@ -215,9 +223,9 @@ class TestDataSeeder extends Seeder
);
// and because we have no filters, some repetitions:
// LimitRepetition::create(['budget_limit_id' => $groceriesLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 201]);
// LimitRepetition::create(['budget_limit_id' => $billsLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 202]);
// LimitRepetition::create(['budget_limit_id' => $deleteMeLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 203]);
// LimitRepetition::create(['budget_limit_id' => $groceriesLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 201]);
// LimitRepetition::create(['budget_limit_id' => $billsLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 202]);
// LimitRepetition::create(['budget_limit_id' => $deleteMeLimit->id, 'startdate' => $this->som, 'enddate' => $this->eom, 'amount' => 203]);
}
/**

9
public/js/reports.js Normal file
View File

@@ -0,0 +1,9 @@
if (typeof(google) != 'undefined') {
google.setOnLoadCallback(drawChart);
function drawChart() {
googleColumnChart('chart/reports/income-expenses/' + year, 'income-expenses-chart');
googleColumnChart('chart/reports/income-expenses-sum/' + year, 'income-expenses-sum-chart')
googleStackedColumnChart('chart/budgets/spending/' + year, 'budgets');
}
}

View File

@@ -0,0 +1,156 @@
@extends('layouts.default')
@section('content')
{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $date) }}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<table class="table table-bordered table-striped">
<tr>
<th>Account</th>
<th>Start of month</th>
<th>Current balance</th>
<th>Spent</th>
</tr>
@foreach($accounts as $account)
<tr>
<td><a href="{{route('accounts.show',$account->id)}}">{{{$account->name}}}</a></td>
<td>{{Amount::format($account->startBalance)}}</td>
<td>{{Amount::format($account->endBalance)}}</td>
<td>{{Amount::format($account->startBalance - $account->endBalance,false)}}</td>
</tr>
@endforeach
</table>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<table class="table table-bordered table-striped">
<tr>
<th colspan="2">Budgets</th>
<?php
$accountSums = [];
?>
@foreach($accounts as $account)
<th><a href="{{route('accounts.show',$account->id)}}">{{{$account->name}}}</a></th>
<?php
$accountSums[$account->id] = 0;
?>
@endforeach
<th colspan="2">
Left in budget
</th>
</tr>
@foreach($budgets as $id => $budget)
<tr>
<td>{{{$budget['name']}}}
@if($id == 0)
<i class="fa fa-fw fa-question-circle" data-toggle="tooltip" data-placement="top" title="The calculation used here is slightly different from the row below. The numbers should match."></i>
@endif
</td>
<td>{{Amount::format($budget['amount'])}}</td>
<?php $spent = 0;?>
@foreach($accounts as $account)
@if(isset($account->budgetInformation[$id]))
<td>
@if($id == 0)
<a href="#">{{Amount::format($account->budgetInformation[$id]['amount'])}}</a>
@else
{{Amount::format($account->budgetInformation[$id]['amount'])}}
@endif
</td>
<?php
$spent += floatval($account->budgetInformation[$id]['amount']);
$accountSums[$account->id] += floatval($account->budgetInformation[$id]['amount']);
?>
@else
<td>{{Amount::format(0)}}</td>
@endif
@endforeach
<td>{{Amount::format($budget['amount'] + $budget['spent'])}}</td>
<td>{{Amount::format($budget['amount'] + $spent)}}</td>
</tr>
@endforeach
<tr>
<td colspan="2">Without budget
<i class="fa fa-fw fa-question-circle" data-toggle="tooltip" data-placement="top" title="The calculation used here is slightly different from the row above. The numbers should match."></i>
</td>
@foreach($accounts as $account)
@if(isset($account->budgetInformation[0]))
<td>
<a href="#">{{Amount::format($account->budgetInformation[0]['amount'])}}</a>
</td>
@else
<td>{{Amount::format(0)}}</td>
@endif
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">Balanced by transfers</td>
@foreach($accounts as $account)
<td>
<a href="#">{{Amount::format($account->balancedAmount)}}</a>
</td>
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
<!--
<tr>
<td colspan="2">Balancing transfers</td>
@foreach($accounts as $account)
<td>{{Amount::format(0)}}</td>
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">Income</td>
@foreach($accounts as $account)
<td>{{Amount::format(0)}}</td>
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
--->
<tr>
<td colspan="2">Left unbalanced</td>
@foreach($accounts as $account)
<?php
$accountSums[$account->id] += $account->balancedAmount;
?>
@if(isset($account->budgetInformation[0]))
<td>
<a href="#">{{Amount::format($account->budgetInformation[0]['amount'] + $account->balancedAmount)}}</a>
</td>
@else
<td>{{Amount::format(0)}}</td>
@endif
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2"><em>Sum</em></td>
@foreach($accounts as $account)
<td>{{Amount::format($accountSums[$account->id])}}</td>
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<td colspan="2">Expected balance</td>
@foreach($accounts as $account)
<td>{{Amount::format($account->startBalance + $accountSums[$account->id])}}</td>
@endforeach
<td colspan="2">&nbsp;</td>
</tr>
</table>
</div>
</div>
<!-- modal to show various budget information -->
<div class="modal fade" id="budgetModal">
</div>
@stop
@section('scripts')
{{HTML::script('assets/javascript/firefly/reports.js')}}
@stop

View File

@@ -0,0 +1,50 @@
@extends('layouts.default')
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) !!}
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
Yearly reports
</div>
<div class="panel-body">
<ul>
@foreach($years as $year)
<li><a href="{{route('reports.year',$year)}}">{{$year}}</a></li>
@endforeach
</ul>
</div>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
Monthly reports
</div>
<div class="panel-body">
<ul>
@foreach($months as $month)
<li><a href="{{route('reports.month',[$month['year'],$month['month']])}}">{{$month['formatted']}}</a></li>
@endforeach
</ul>
</div>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
Budget reports
</div>
<div class="panel-body">
<ul>
@foreach($months as $month)
<li><a href="{{route('reports.budget',[$month['year'],$month['month']])}}">{{$month['formatted']}}</a></li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
@stop

View File

@@ -0,0 +1,235 @@
@extends('layouts.default')
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $date) !!}
<div class="row">
<div class="col-lg-5 col-md-5 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Income</div>
<table class="table table-bordered">
<?php $tableSum = 0;?>
@foreach($income as $entry)
<tr>
<td>
@if($entry->encrypted === true)
<a href="{{route('transactions.show',$entry->id)}}" title="{{{Crypt::decrypt($entry->description)}}}">{{{Crypt::decrypt($entry->description)}}}</a>
@else
<a href="{{route('transactions.show',$entry->id)}}" title="{{{$entry->description}}}">{{{$entry->description}}}</a>
@endif
</td>
<td>
<?php $tableSum += floatval($entry->amount);?>
@if($entry->type == 'Withdrawal')
<span class="text-danger">{{Amount::format($entry->amount,false)}}</span>
@endif
@if($entry->type == 'Deposit')
<span class="text-success">{{Amount::format($entry->amount,false)}}</span>
@endif
@if($entry->type == 'Transfer')
<span class="text-info">{{Amount::format($entry->amount,false)}}</span>
@endif
</td>
<td>
{{$entry->date->format('j F Y')}}
</td>
<td>
<a href="{{route('accounts.show',$entry->account_id)}}">{{{$entry->name}}}</a>
</td>
</tr>
@endforeach
@if(isset($displaySum) && $displaySum === true)
<tr>
<td><em>Sum</em></td>
<td colspan="3">{!! Amount::format($tableSum) !!}</td>
</tr>
@endif
</table>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Expenses (top 10)</div>
<table class="table table-bordered">
<?php $sum = 0;?>
@foreach($expenses as $id => $expense)
<?php $sum += floatval($expense['amount']);?>
<tr>
@if($id > 0)
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
@else
<td><em>{{{$expense['name']}}}</em></td>
@endif
<td>{!! Amount::format($expense['amount']) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sum) !!}</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Sums</div>
<?php
$in = 0;
foreach($income as $entry) {
$in += floatval($entry->transactions[1]->amount);
}
?>
<table class="table table-bordered">
<tr>
<td>In</td>
<td>{!! Amount::format($in) !!}</td>
</tr>
<tr>
<td>Out</td>
<td>{!! Amount::format($sum) !!}</td>
</tr>
<tr>
<td>Difference</td>
<td>{!! Amount::format($in - $sum) !!}</td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Budgets</div>
<table class="table table-bordered">
<tr>
<th>Budget</th>
<th>Envelope</th>
<th>Spent</th>
<th>Left</th>
</tr>
<?php
$sumSpent = 0;
$sumEnvelope = 0;
$sumLeft = 0;
?>
@foreach($budgets as $id => $budget)
<?php
$sumSpent += $budget['spent'];
$sumEnvelope += $budget['amount'];
$sumLeft += $budget['amount'] + $budget['spent'];
?>
<tr>
<td>
@if($id > 0)
<a href="{{route('budgets.show',$id)}}">{{{$budget['name']}}}</a>
@else
<em>{{{$budget['name']}}}</em>
@endif
</td>
<td>{!! Amount::format($budget['amount']) !!}</td>
<td>{!! Amount::format($budget['spent'],false) !!}</td>
<td>{!! Amount::format($budget['amount'] + $budget['spent']) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sumEnvelope) !!}</td>
<td>{!! Amount::format($sumSpent) !!}</td>
<td>{!! Amount::format($sumLeft) !!}</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Categories</div>
<table class="table table-bordered">
<tr>
<th>Category</th>
<th>Spent</th>
</tr>
<?php $sum = 0;?>
@foreach($categories as $id => $category)
<?php $sum += floatval($category['amount']);?>
<tr>
<td>
@if($id > 0)
<a href="{{route('categories.show',$id)}}">{{{$category['name']}}}</a>
@else
<em>{{{$category['name']}}}</em>
@endif
</td>
<td>{!! Amount::format($category['amount'],false) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sum) !!}</td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Accounts</div>
<table class="table table-bordered">
<?php
$sumStart = 0;
$sumEnd = 0;
$sumDiff = 0;
?>
@foreach($accounts as $id => $account)
<?php
$sumStart += $account['startBalance'];
$sumEnd += $account['endBalance'];
$sumDiff += $account['difference'];
?>
<tr>
<td><a href="{{route('accounts.show',$id)}}">{{{$account['name']}}}</a></td>
<td>{!! Amount::format($account['startBalance']) !!}</td>
<td>{!! Amount::format($account['endBalance']) !!}</td>
<td>{!! Amount::format($account['difference']) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sumStart) !!}</td>
<td>{!! Amount::format($sumEnd) !!}</td>
<td>{!! Amount::format($sumDiff) !!}</td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Piggy banks</div>
<div class="panel-body">Body</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Repeated expenses</div>
<div class="panel-body">Body</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Bills</div>
<div class="panel-body">Body</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Outside of budgets</div>
<div class="panel-body">Body</div>
</div>
</div>
</div>
@stop

View File

@@ -0,0 +1,170 @@
@extends('layouts.default')
@section('content')
{!! Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $date) !!}
<div class="row">
<div class="col-lg-10 col-md-8 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
Income vs. expenses
</div>
<div class="panel-body">
<div id="income-expenses-chart"></div>
</div>
</div>
</div>
<div class="col-lg-2 col-md-4 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
Income vs. expenses
</div>
<div class="panel-body">
<div id="income-expenses-sum-chart"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
Account balance
</div>
<table class="table table-bordered table-striped">
<?php
$start = 0;
$end = 0;
$diff = 0;
?>
@foreach($balances as $balance)
<?php
$start += $balance['start'];
$end += $balance['end'];
$diff += ($balance['end']-$balance['start']);
?>
<tr>
<td>
<a href="{{route('accounts.show',$balance['account']->id)}}">{{{$balance['account']->name}}}</a>
@if($balance['shared'])
<small><em>shared</em></small>
@endif
</td>
<td>{!! Amount::format($balance['start']) !!}</td>
<td>{!! Amount::format($balance['end']) !!}</td>
<td>{!! Amount::format($balance['end']-$balance['start']) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum of sums</em></td>
<td>{!! Amount::format($start) !!}</td>
<td>{!! Amount::format($end) !!}</td>
<td>{!! Amount::format($diff) !!}</td>
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Income vs. expense
</div>
<?php
$incomeSum = 0;
$expenseSum = 0;
foreach($groupedIncomes as $income) {
$incomeSum += floatval($income->amount);
}
foreach($groupedExpenses as $exp) {
$expenseSum += floatval($exp['amount']);
}
$incomeSum = floatval($incomeSum*-1);
?>
<table class="table table-bordered table-striped">
<tr>
<td>In</td>
<td>{!! Amount::format($incomeSum) !!}</td>
</tr>
<tr>
<td>Out</td>
<td>{!! Amount::format($expenseSum*-1) !!}</td>
</tr>
<tr>
<td>Difference</td>
<td>{!! Amount::format($incomeSum - $expenseSum) !!}</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-3">
<div class="panel panel-default">
<div class="panel-heading">
Income
</div>
<table class="table">
<?php $sum = 0;?>
@foreach($groupedIncomes as $income)
<?php $sum += floatval($income->amount)*-1;?>
<tr>
<td><a href="{{route('accounts.show',$income->account_id)}}">{{{$income->name}}}</a></td>
<td>{!! Amount::format(floatval($income->amount)*-1) !!}</td>
</tr>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sum) !!}</td>
</tr>
</table>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-3">
<div class="panel panel-default">
<div class="panel-heading">
Expenses
</div>
<table class="table">
<?php $sum = 0;?>
@foreach($groupedExpenses as $id => $expense)
<tr>
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
<td>{!! Amount::format(floatval($expense['amount'])*-1) !!}</td>
</tr>
<?php $sum += floatval($expense['amount'])*-1;?>
@endforeach
<tr>
<td><em>Sum</em></td>
<td>{!! Amount::format($sum) !!}</td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
Budgets
</div>
<div class="panel-body">
<div id="budgets"></div>
</div>
</div>
</div>
</div>
@stop
@section('scripts')
<!-- load the libraries and scripts necessary for Google Charts: -->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="js/gcharts.options.js"></script>
<script type="text/javascript" src="js/gcharts.js"></script>
<script type="text/javascript">
var year = '{{$year}}';
var currencyCode = '{{Amount::getCurrencyCode()}}';
</script>
<script type="text/javascript" src="js/reports.js"></script>
@stop