mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2024-12-31 19:27:51 -06:00
Greatly expanded report functionality.
This commit is contained in:
parent
9f23462c42
commit
4944b233b6
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Database\Account\Account as AccountRepository;
|
||||
use FireflyIII\Database\TransactionJournal\TransactionJournal as TransactionJournalRepository;
|
||||
use FireflyIII\Report\ReportInterface as Report;
|
||||
|
||||
@ -10,31 +9,53 @@ use FireflyIII\Report\ReportInterface as Report;
|
||||
*/
|
||||
class ReportController extends BaseController
|
||||
{
|
||||
/** @var AccountRepository */
|
||||
protected $_accounts;
|
||||
|
||||
/** @var \FireflyIII\Database\Budget\Budget */
|
||||
protected $_budgets;
|
||||
/** @var TransactionJournalRepository */
|
||||
protected $_journals;
|
||||
|
||||
/** @var Report */
|
||||
protected $_repository;
|
||||
|
||||
/**
|
||||
* @param AccountRepository $accounts
|
||||
* @param TransactionJournalRepository $journals
|
||||
* @param Report $repository
|
||||
*/
|
||||
public function __construct(AccountRepository $accounts, TransactionJournalRepository $journals, Report $repository)
|
||||
public function __construct(TransactionJournalRepository $journals, Report $repository)
|
||||
{
|
||||
$this->_accounts = $accounts;
|
||||
$this->_journals = $journals;
|
||||
$this->_repository = $repository;
|
||||
/** @var \FireflyIII\Database\Budget\Budget _budgets */
|
||||
$this->_budgets = App::make('FireflyIII\Database\Budget\Budget');
|
||||
|
||||
|
||||
View::share('title', 'Reports');
|
||||
View::share('mainTitleIcon', 'fa-line-chart');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $month
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function budget($year = '2014', $month = '1')
|
||||
{
|
||||
try {
|
||||
new Carbon($year . '-' . $month . '-01');
|
||||
} catch (Exception $e) {
|
||||
View::make('error')->with('message', 'Invalid date');
|
||||
}
|
||||
$date = new Carbon($year . '-' . $month . '-01');
|
||||
$dayEarly = clone $date;
|
||||
$dayEarly = $dayEarly->subDay();
|
||||
$accounts = $this->_repository->getAccountListBudgetOverview($date);
|
||||
$budgets = $this->_repository->getBudgetsForMonth($date);
|
||||
|
||||
return View::make('reports.budget', compact('accounts', 'budgets', 'dayEarly'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -62,7 +83,7 @@ class ReportController extends BaseController
|
||||
} catch (Exception $e) {
|
||||
View::make('error')->with('message', 'Invalid date');
|
||||
}
|
||||
$date = new Carbon($year . '-' . $month . '-01');
|
||||
$date = new Carbon($year . '-' . $month . '-01');
|
||||
$subTitle = 'Report for ' . $date->format('F Y');
|
||||
$subTitleIcon = 'fa-calendar';
|
||||
$displaySum = true; // to show sums in report.
|
||||
@ -79,61 +100,6 @@ class ReportController extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $year
|
||||
* @param $month
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function unbalanced($year, $month)
|
||||
{
|
||||
try {
|
||||
new Carbon($year . '-' . $month . '-01');
|
||||
} catch (Exception $e) {
|
||||
App::abort(500);
|
||||
}
|
||||
$start = new Carbon($year . '-' . $month . '-01');
|
||||
$end = clone $start;
|
||||
$title = 'Reports';
|
||||
$subTitle = 'Unbalanced transactions in ' . $start->format('F Y');
|
||||
$mainTitleIcon = 'fa-line-chart';
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$end->endOfMonth();
|
||||
|
||||
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $journalRepository */
|
||||
$journalRepository = App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
|
||||
$journals = $journalRepository->getInDateRange($start, $end);
|
||||
|
||||
$withdrawals = $journals->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
$relations = $journal->transactiongroups()->where('relation', 'balance')->count();
|
||||
$budgets = $journal->budgets()->count();
|
||||
$type = $journal->transactionType->type;
|
||||
if ($type == 'Withdrawal' && $budgets == 0 && $relations == 0) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
$deposits = $journals->filter(
|
||||
function (TransactionJournal $journal) {
|
||||
$relations = $journal->transactiongroups()->where('relation', 'balance')->count();
|
||||
$budgets = $journal->budgets()->count();
|
||||
$type = $journal->transactionType->type;
|
||||
if ($type == 'Deposit' && $budgets == 0 && $relations == 0) {
|
||||
return $journal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
$journals = $withdrawals->merge($deposits);
|
||||
|
||||
return View::make('reports.unbalanced', compact('start', 'end', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon', 'journals'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $year
|
||||
*
|
||||
|
@ -9,7 +9,6 @@ use FireflyIII\Database\TransactionJournal\TransactionJournal as JournalReposito
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use stdClass;
|
||||
|
||||
// todo add methods to interface
|
||||
|
||||
@ -47,8 +46,6 @@ class Report implements ReportInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO Used in yearly report, so not ready for cleanup.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param int $limit
|
||||
@ -57,37 +54,49 @@ class Report implements ReportInterface
|
||||
*/
|
||||
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15)
|
||||
{
|
||||
return \TransactionJournal::
|
||||
leftJoin(
|
||||
'transactions as t_from', function ($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 ($join) {
|
||||
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions as t_to', function ($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 ($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('transaction_types.type', 'Withdrawal')
|
||||
->where('acm_from.data', '!=', '"sharedExpense"')
|
||||
->before($end)->after($start)
|
||||
->where('transaction_journals.user_id', \Auth::user()->id)
|
||||
->groupBy('account_id')->orderBy('sum', 'DESC')->limit($limit)
|
||||
->get(['t_to.account_id as account_id', 'ac_to.name as name', \DB::Raw('SUM(t_to.amount) as `sum`')]);
|
||||
$result = $this->_queries->journalsByExpenseAccount($start, $end);
|
||||
$array = $this->_helper->makeArray($result);
|
||||
$limited = $this->_helper->limitArray($array, $limit);
|
||||
|
||||
return $limited;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the users shared and non-shared accounts combined with various meta-data
|
||||
* to display the amount of money spent that month compared to what's been spend within
|
||||
* budgets.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccountListBudgetOverview(Carbon $date)
|
||||
{
|
||||
$start = clone $date;
|
||||
$start->startOfMonth();
|
||||
$end = clone $date;
|
||||
$end->endOfMonth();
|
||||
$start->subDay();
|
||||
$accounts = $this->_queries->getAllAccounts($start, $end);
|
||||
|
||||
$accounts->each(
|
||||
function (\Account $account) use ($start, $end) {
|
||||
$budgets = $this->_queries->getBudgetSummary($account, $start, $end);
|
||||
$balancedAmount = $this->_queries->balancedTransactionsSum($account, $start, $end);
|
||||
$array = [];
|
||||
foreach ($budgets as $budget) {
|
||||
$id = intval($budget->id);
|
||||
$data = $budget->toArray();
|
||||
$array[$id] = $data;
|
||||
}
|
||||
$account->budgetInformation = $array;
|
||||
$account->balancedAmount = $balancedAmount;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
return $accounts;
|
||||
|
||||
}
|
||||
|
||||
@ -191,78 +200,27 @@ class Report implements ReportInterface
|
||||
$start->startOfMonth();
|
||||
$end = clone $date;
|
||||
$end->endOfMonth();
|
||||
$userId = $this->_accounts->getUser()->id;
|
||||
|
||||
$set = \TransactionJournal::
|
||||
leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_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(
|
||||
'transactions AS otherTransactions', function (JoinClause $join) {
|
||||
$join->on('transaction_journals.id', '=', 'otherTransactions.transaction_journal_id')->where(
|
||||
'otherTransactions.amount', '<', 0
|
||||
);
|
||||
}
|
||||
)
|
||||
->leftJoin('accounts as otherAccounts', 'otherAccounts.id', '=', 'otherTransactions.account_id')
|
||||
->leftJoin(
|
||||
'account_meta', function (JoinClause $join) {
|
||||
$join->on('otherAccounts.id', '=', 'account_meta.account_id')->where('account_meta.name', '=', 'accountRole');
|
||||
}
|
||||
)
|
||||
->where('date', '>=', $start->format('Y-m-d'))
|
||||
->where('date', '<=', $end->format('Y-m-d'))
|
||||
->where('account_meta.data', '!=', '"sharedExpense"')
|
||||
->where('transaction_types.type', 'Withdrawal')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $userId)
|
||||
->groupBy('account_id')
|
||||
->orderBy('sum', 'ASC')
|
||||
->get(
|
||||
[
|
||||
'transactions.account_id',
|
||||
'accounts.name',
|
||||
\DB::Raw('SUM(`transactions`.`amount`) * -1 AS `sum`')
|
||||
]
|
||||
);
|
||||
$transfers = $this->getTransfersToSharedGroupedByAccounts($date);
|
||||
// merge $transfers into $set
|
||||
$set = $this->_queries->journalsByExpenseAccount($start, $end);
|
||||
$expenses = $this->_helper->makeArray($set);
|
||||
|
||||
$alt = $this->_queries->sharedExpenses($start, $end);
|
||||
$transfers = $this->_helper->makeArray($alt);
|
||||
|
||||
$expenses[-1] = [
|
||||
'amount' => 0,
|
||||
'name' => 'Transfers to shared',
|
||||
'spent' => 0
|
||||
];
|
||||
|
||||
foreach ($transfers as $transfer) {
|
||||
if (!is_null($transfer->account_id)) {
|
||||
$set->push($transfer);
|
||||
}
|
||||
}
|
||||
// sort the list.
|
||||
$set = $set->sortBy(
|
||||
function ($entry) {
|
||||
return floatval($entry->sum);
|
||||
}
|
||||
);
|
||||
$return = new Collection;
|
||||
$bottom = new stdClass();
|
||||
$bottom->name = 'Others';
|
||||
$bottom->account_id = 0;
|
||||
$bottom->sum = 0;
|
||||
|
||||
$count = 0;
|
||||
foreach ($set as $entry) {
|
||||
if ($count < $limit) {
|
||||
$return->push($entry);
|
||||
} else {
|
||||
$bottom->sum += floatval($entry->sum);
|
||||
}
|
||||
$count++;
|
||||
$expenses[-1]['amount'] += $transfer['amount'];
|
||||
}
|
||||
|
||||
$return->push($bottom);
|
||||
$expenses = $this->_helper->sortArray($expenses);
|
||||
$limited = $this->_helper->limitArray($expenses, $limit);
|
||||
|
||||
return $return;
|
||||
return $limited;
|
||||
|
||||
}
|
||||
|
||||
@ -280,8 +238,8 @@ class Report implements ReportInterface
|
||||
$end->endOfMonth();
|
||||
$userId = $this->_accounts->getUser()->id;
|
||||
|
||||
$list = \TransactionJournal::withRelevantData()
|
||||
->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
|
||||
$list = \TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
||||
->leftJoin(
|
||||
'account_meta', function (JoinClause $join) {
|
||||
@ -291,6 +249,7 @@ class Report implements ReportInterface
|
||||
->transactionTypes(['Deposit'])
|
||||
->where('transaction_journals.user_id', $userId)
|
||||
->where('transactions.amount', '>', 0)
|
||||
->where('transaction_journals.user_id', \Auth::user()->id)
|
||||
->where('account_meta.data', '!=', '"sharedExpense"')
|
||||
->orderBy('date', 'ASC')
|
||||
->before($end)->after($start)->get(['transaction_journals.*']);
|
||||
@ -354,49 +313,6 @@ class Report implements ReportInterface
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getTransfersToSharedGroupedByAccounts(Carbon $date)
|
||||
{
|
||||
$start = clone $date;
|
||||
$start->startOfMonth();
|
||||
$end = clone $date;
|
||||
$end->endOfMonth();
|
||||
|
||||
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"')
|
||||
->where('date', '>=', $start->format('Y-m-d'))
|
||||
->where('date', '<=', $end->format('Y-m-d'))
|
||||
->where('transaction_types.type', 'Transfer')
|
||||
->where('transaction_journals.user_id', \Auth::user()->id)
|
||||
->groupBy('accounts.name')
|
||||
->get(
|
||||
[
|
||||
'transactions.account_id',
|
||||
'accounts.name',
|
||||
\DB::Raw('SUM(`transactions`.`amount`) * -1 AS `sum`')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
*
|
||||
|
@ -116,4 +116,25 @@ class ReportHelper implements ReportHelperInterface
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
}
|
@ -52,4 +52,13 @@ interface ReportHelperInterface
|
||||
*/
|
||||
public function sortNegativeArray(array $array);
|
||||
|
||||
/**
|
||||
* Sort an array where all 'amount' keys are positive floats.
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sortArray(array $array);
|
||||
|
||||
}
|
@ -22,33 +22,15 @@ interface ReportInterface
|
||||
public function expensesGroupedByAccount(Carbon $start, Carbon $end, $limit = 15);
|
||||
|
||||
/**
|
||||
* Gets all the users shared and non-shared accounts combined with various meta-data
|
||||
* to display the amount of money spent that month compared to what's been spend within
|
||||
* budgets.
|
||||
*
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBudgetsForMonth(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getTransfersToSharedGroupedByAccounts(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBanksForMonth(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
* @param int $limit
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategoriesForMonth(Carbon $date, $limit = 15);
|
||||
public function getAccountListBudgetOverview(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
@ -57,14 +39,28 @@ interface ReportInterface
|
||||
*/
|
||||
public function getAccountsForMonth(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getBudgetsForMonth(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
* @param int $limit
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategoriesForMonth(Carbon $date, $limit = 15);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
* @param int $limit
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getExpenseGroupedForMonth(Carbon $date, $limit = 15);
|
||||
|
||||
public function getExpenseGroupedForMonth(Carbon $date, $limit = 15);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
@ -74,6 +70,13 @@ interface ReportInterface
|
||||
*/
|
||||
public function getIncomeForMonth(Carbon $date, $shared = false);
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBanksForMonth(Carbon $date);
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
*
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace FireflyIII\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@ -30,7 +31,7 @@ class ReportQuery implements ReportQueryInterface
|
||||
->whereIn('account_types.type', ['Default account', 'Cash account', 'Asset account'])
|
||||
->where('active', 1)
|
||||
->where(
|
||||
function ($query) {
|
||||
function (Builder $query) {
|
||||
$query->where('account_meta.data', '!=', '"sharedExpense"');
|
||||
$query->orWhereNull('account_meta.data');
|
||||
}
|
||||
@ -38,6 +39,114 @@ class ReportQuery implements ReportQueryInterface
|
||||
->get(['accounts.*']);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will get a list of all expenses in a certain time period that have no budget
|
||||
* and are balanced by a transfer to make up for it.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function balancedTransactionsList(\Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
|
||||
$set = \TransactionJournal::
|
||||
leftJoin('transaction_group_transaction_journal', 'transaction_group_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin(
|
||||
'transaction_group_transaction_journal as otherFromGroup', function (JoinClause $join) {
|
||||
$join->on('otherFromGroup.transaction_group_id', '=', 'transaction_group_transaction_journal.transaction_group_id')
|
||||
->on('otherFromGroup.transaction_journal_id', '!=', 'transaction_journals.id');
|
||||
}
|
||||
)
|
||||
->leftJoin('transaction_journals as otherJournals', 'otherJournals.id', '=', 'otherFromGroup.transaction_journal_id')
|
||||
->leftJoin('transaction_types', 'transaction_types.id', '=', 'otherJournals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions', function (JoinClause $join) {
|
||||
$join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('amount', '>', 0);
|
||||
}
|
||||
)
|
||||
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
|
||||
->before($end)
|
||||
->after($start)
|
||||
->where('transaction_types.type', 'Withdrawal')
|
||||
->where('transaction_journals.user_id', \Auth::user()->id)
|
||||
->whereNull('budget_transaction_journal.budget_id')
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->whereNull('otherJournals.deleted_at')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
|
||||
->groupBy('transaction_journals.id')
|
||||
->get(
|
||||
[
|
||||
'transaction_journals.id as transferId',
|
||||
'transaction_journals.description as transferDescription',
|
||||
'transaction_group_transaction_journal.transaction_group_id as groupId',
|
||||
'otherFromGroup.transaction_journal_id as expenseId',
|
||||
'otherJournals.description as expenseDescription',
|
||||
'transactions.amount'
|
||||
]
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will sum up all expenses in a certain time period that have no budget
|
||||
* and are balanced by a transfer to make up for it.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function balancedTransactionsSum(\Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$list = $this->balancedTransactionsList($account, $start, $end);
|
||||
$sum = 0;
|
||||
foreach ($list as $entry) {
|
||||
$sum += floatval($entry->amount);
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a users accounts combined with various meta-data related to the start and end date.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAllAccounts(Carbon $start, Carbon $end)
|
||||
{
|
||||
$set = \Auth::user()->accounts()
|
||||
->accountTypeIn(['Default account', 'Asset account', 'Cash account'])
|
||||
->leftJoin(
|
||||
'account_meta', function (JoinClause $join) {
|
||||
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
|
||||
}
|
||||
)
|
||||
->where(
|
||||
function (Builder $query) {
|
||||
$query->where('account_meta.data', '!=', '"sharedExpense"');
|
||||
$query->orWhereNull('account_meta.data');
|
||||
}
|
||||
)
|
||||
->get(['accounts.*']);
|
||||
$set->each(
|
||||
function (\Account $account) use ($start, $end) {
|
||||
$account->startBalance = \Steam::balance($account, $start);
|
||||
$account->endBalance = \Steam::balance($account, $end);
|
||||
}
|
||||
);
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all budgets and if present, the amount of the current BudgetLimit
|
||||
* as well
|
||||
@ -57,6 +166,41 @@ class ReportQuery implements ReportQueryInterface
|
||||
->get(['budgets.*', 'budget_limits.amount as amount']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs a summary of all expenses grouped by budget, related to the account.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBudgetSummary(\Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$set = \TransactionJournal::
|
||||
leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
|
||||
->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')
|
||||
->before($end)
|
||||
->after($start)
|
||||
->where('accounts.id', $account->id)
|
||||
->where('transaction_journals.user_id', \Auth::user()->id)
|
||||
->where('transaction_types.type', 'Withdrawal')
|
||||
->groupBy('budgets.id')
|
||||
->orderBy('budgets.id')
|
||||
->get(['budgets.id', 'budgets.name', \DB::Raw('SUM(`transactions`.`amount`) as `amount`')]);
|
||||
|
||||
return $set;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of expenses grouped by the budget they were filed under.
|
||||
*
|
||||
@ -140,6 +284,38 @@ class ReportQuery implements ReportQueryInterface
|
||||
*/
|
||||
public function journalsByExpenseAccount(Carbon $start, Carbon $end)
|
||||
{
|
||||
return \TransactionJournal::
|
||||
leftJoin(
|
||||
'transactions as t_from', function ($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 ($join) {
|
||||
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions as t_to', function ($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 ($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('transaction_types.type', 'Withdrawal')
|
||||
->where('acm_from.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`')]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,14 +22,49 @@ interface ReportQueryInterface
|
||||
public function accountList();
|
||||
|
||||
/**
|
||||
* Gets a list of expenses grouped by the budget they were filed under.
|
||||
* Get a users accounts combined with various meta-data related to the start and end date.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function journalsByBudget(Carbon $start, Carbon $end);
|
||||
public function getAllAccounts(Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Grabs a summary of all expenses grouped by budget, related to the account.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBudgetSummary(\Account $account, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* This method will sum up all expenses in a certain time period that have no budget
|
||||
* and are balanced by a transfer to make up for it.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function balancedTransactionsSum(\Account $account, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* This method will get a list of all expenses in a certain time period that have no budget
|
||||
* and are balanced by a transfer to make up for it.
|
||||
*
|
||||
* @param \Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function balancedTransactionsList(\Account $account, Carbon $start, Carbon $end);
|
||||
|
||||
/**
|
||||
* Gets a list of all budgets and if present, the amount of the current BudgetLimit
|
||||
@ -41,6 +76,16 @@ interface ReportQueryInterface
|
||||
*/
|
||||
public function getAllBudgets(Carbon $date);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -63,6 +108,16 @@ interface ReportQueryInterface
|
||||
*/
|
||||
public function journalsByExpenseAccount(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
|
||||
@ -75,15 +130,4 @@ interface ReportQueryInterface
|
||||
*/
|
||||
public function sharedExpensesByCategory(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);
|
||||
|
||||
}
|
@ -111,7 +111,7 @@ class TransactionJournal extends Eloquent
|
||||
*/
|
||||
public function scopeAfter($query, Carbon $date)
|
||||
{
|
||||
return $query->where('date', '>=', $date->format('Y-m-d'));
|
||||
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +122,7 @@ class TransactionJournal extends Eloquent
|
||||
*/
|
||||
public function scopeBefore($query, Carbon $date)
|
||||
{
|
||||
return $query->where('date', '<=', $date->format('Y-m-d'));
|
||||
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,6 +248,7 @@ Route::group(
|
||||
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/unbalanced/{year}/{month}', ['uses' => 'ReportController@unbalanced', 'as' => 'reports.unbalanced']);
|
||||
|
||||
// reminder controller
|
||||
|
129
app/views/reports/budget.blade.php
Normal file
129
app/views/reports/budget.blade.php
Normal file
@ -0,0 +1,129 @@
|
||||
@extends('layouts.default')
|
||||
@section('content')
|
||||
{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName()) }}
|
||||
<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>{{mf($account->startBalance)}}</td>
|
||||
<td>{{mf($account->endBalance)}}</td>
|
||||
<td>{{mf($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']}}}</td>
|
||||
<td>{{mf($budget['amount'])}}</td>
|
||||
<?php $spent = 0;?>
|
||||
@foreach($accounts as $account)
|
||||
@if(isset($account->budgetInformation[$id]))
|
||||
<td>{{mf($account->budgetInformation[$id]['amount'])}}</td>
|
||||
<?php
|
||||
$spent += floatval($account->budgetInformation[$id]['amount']);
|
||||
$accountSums[$account->id] += floatval($account->budgetInformation[$id]['amount']);
|
||||
?>
|
||||
@else
|
||||
<td>{{mf(0)}}</td>
|
||||
@endif
|
||||
@endforeach
|
||||
<td>{{mf($budget['amount'] + $budget['spent'])}}</td>
|
||||
<td>{{mf($budget['amount'] + $spent)}}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr>
|
||||
<td colspan="2">Without budget</td>
|
||||
@foreach($accounts as $account)
|
||||
@if(isset($account->budgetInformation[0]))
|
||||
<td>{{mf($account->budgetInformation[0]['amount'])}}</td>
|
||||
@else
|
||||
<td>{{mf(0)}}</td>
|
||||
@endif
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">Balanced by transfers</td>
|
||||
@foreach($accounts as $account)
|
||||
<td>{{mf($account->balancedAmount)}}</td>
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<!--
|
||||
<tr>
|
||||
<td colspan="2">Balancing transfers</td>
|
||||
@foreach($accounts as $account)
|
||||
<td>{{mf(0)}}</td>
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">Income</td>
|
||||
@foreach($accounts as $account)
|
||||
<td>{{mf(0)}}</td>
|
||||
@endforeach
|
||||
<td colspan="2"> </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>{{mf($account->budgetInformation[0]['amount'] + $account->balancedAmount)}}</td>
|
||||
@else
|
||||
<td>{{mf(0)}}</td>
|
||||
@endif
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><em>Sum</em></td>
|
||||
@foreach($accounts as $account)
|
||||
<td>{{mf($accountSums[$account->id])}}</td>
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">Expected balance</td>
|
||||
@foreach($accounts as $account)
|
||||
<td>{{mf($account->startBalance + $accountSums[$account->id])}}</td>
|
||||
@endforeach
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
@ -31,5 +31,20 @@
|
||||
</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
|
@ -13,15 +13,15 @@
|
||||
<div class="panel-heading">Expenses (top 10)</div>
|
||||
<table class="table table-bordered">
|
||||
<?php $sum = 0;?>
|
||||
@foreach($expenses as $expense)
|
||||
<?php $sum += floatval($expense->sum);?>
|
||||
@foreach($expenses as $id => $expense)
|
||||
<?php $sum += floatval($expense['amount']);?>
|
||||
<tr>
|
||||
@if($expense->account_id != 0)
|
||||
<td><a href="{{route('accounts.show',$expense->account_id)}}">{{{$expense->name}}}</a></td>
|
||||
@if($id > 0)
|
||||
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
|
||||
@else
|
||||
<td><em>{{{$expense->name}}}</em></td>
|
||||
<td><em>{{{$expense['name']}}}</em></td>
|
||||
@endif
|
||||
<td>{{mf($expense->sum)}}</td>
|
||||
<td>{{mf($expense['amount'])}}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr>
|
||||
@ -51,7 +51,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Difference</td>
|
||||
<td>{{mf($sum + $in)}}</td>
|
||||
<td>{{mf($in - $sum)}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,56 +0,0 @@
|
||||
@extends('layouts.default')
|
||||
@section('content')
|
||||
{{ Breadcrumbs::renderIfExists(Route::getCurrentRoute()->getName(), $start) }}
|
||||
@if(count($journals) == 0)
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-4 col-sm-6">
|
||||
<p class="text-success">Everything accounted for.</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if(count($journals) > 0)
|
||||
<div class="row">
|
||||
<div class="col-lg-4 col-md-4 col-sm-6">
|
||||
<h3>Withdrawals</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@foreach($journals as $journal)
|
||||
<div class="col-lg-4 col-md-4 col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
@if($journal->transactiontype->type == 'Withdrawal')
|
||||
<span class="glyphicon glyphicon-arrow-left" title="Withdrawal"></span>
|
||||
@endif
|
||||
@if($journal->transactiontype->type == 'Deposit')
|
||||
<span class="glyphicon glyphicon-arrow-right" title="Deposit"></span>
|
||||
@endif
|
||||
@if($journal->transactiontype->type == 'Transfer')
|
||||
<span class="glyphicon glyphicon-resize-full" title="Transfer"></span>
|
||||
@endif
|
||||
@if($journal->transactiontype->type == 'Opening balance')
|
||||
<span class="glyphicon glyphicon-ban-circle" title="Opening balance"></span>
|
||||
@endif
|
||||
<a href="{{route('transactions.show',$journal->id)}}">{{{$journal->description}}}</a>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>Spent {{mf($journal->getAmount())}}</p>
|
||||
<p class="text-danger">No counter transaction!</p>
|
||||
<p>
|
||||
<a href="#" data-id="{{{$journal->id}}}" class="relateTransaction btn btn-default">Add counter transaction</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@stop
|
||||
@section('scripts')
|
||||
<script type="text/javascript">
|
||||
var currencyCode = '{{getCurrencyCode()}}';
|
||||
</script>
|
||||
{{HTML::script('assets/javascript/firefly/reports.js')}}
|
||||
{{HTML::script('assets/javascript/firefly/related-manager.js')}}
|
||||
@stop
|
@ -84,10 +84,10 @@
|
||||
Expenses
|
||||
</div>
|
||||
<table class="table">
|
||||
@foreach($groupedExpenses as $expense)
|
||||
@foreach($groupedExpenses as $id => $expense)
|
||||
<tr>
|
||||
<td><a href="{{route('accounts.show',$expense->account_id)}}">{{{$expense->name}}}</a></td>
|
||||
<td>{{mf(floatval($expense->sum)*-1)}}</td>
|
||||
<td><a href="{{route('accounts.show',$id)}}">{{{$expense['name']}}}</a></td>
|
||||
<td>{{mf(floatval($expense['amount'])*-1)}}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
|
Loading…
Reference in New Issue
Block a user