mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2024-11-27 11:20:39 -06:00
444 lines
16 KiB
PHP
444 lines
16 KiB
PHP
<?php
|
|
|
|
namespace FireflyIII\Report;
|
|
|
|
use Carbon\Carbon;
|
|
use FireflyIII\Database\Account\Account as AccountRepository;
|
|
use FireflyIII\Database\SwitchUser;
|
|
use FireflyIII\Database\TransactionJournal\TransactionJournal as JournalRepository;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Query\JoinClause;
|
|
use Illuminate\Support\Collection;
|
|
|
|
// todo add methods to interface
|
|
|
|
/**
|
|
* Class Report
|
|
*
|
|
*
|
|
* @package FireflyIII\Report
|
|
*/
|
|
class Report implements ReportInterface
|
|
{
|
|
|
|
use SwitchUser;
|
|
|
|
/** @var AccountRepository */
|
|
protected $_accounts;
|
|
/** @var \FireflyIII\Report\ReportHelperInterface */
|
|
protected $_helper;
|
|
/** @var JournalRepository */
|
|
protected $_journals;
|
|
/** @var \FireflyIII\Report\ReportQueryInterface */
|
|
protected $_queries;
|
|
|
|
/**
|
|
* @param AccountRepository $accounts
|
|
* @param JournalRepository $journals
|
|
*/
|
|
public function __construct(AccountRepository $accounts, JournalRepository $journals)
|
|
{
|
|
$this->_accounts = $accounts;
|
|
$this->_journals = $journals;
|
|
$this->_queries = \App::make('FireflyIII\Report\ReportQueryInterface');
|
|
$this->_helper = \App::make('FireflyIII\Report\ReportHelperInterface');
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $date
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getAccountsForMonth(Carbon $date)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth()->subDay();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
\Log::debug('Monthly report account dates: start:[' . $start->format('Y-m-d') . '] and end:[' . $end->format('Y-m-d') . ']');
|
|
$list = $this->_queries->accountList();
|
|
$accounts = [];
|
|
/** @var \Account $account */
|
|
foreach ($list as $account) {
|
|
$id = intval($account->id);
|
|
$accounts[$id] = [
|
|
'name' => $account->name,
|
|
'startBalance' => \Steam::balance($account, $start),
|
|
'endBalance' => \Steam::balance($account, $end)
|
|
];
|
|
|
|
$accounts[$id]['difference'] = $accounts[$id]['endBalance'] - $accounts[$id]['startBalance'];
|
|
}
|
|
|
|
return $accounts;
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $date
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function getBudgetsForMonth(Carbon $date)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
// all budgets
|
|
$set = $this->_queries->getAllBudgets($date);
|
|
$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
|
|
* @param int $limit
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getCategoriesForMonth(Carbon $date, $limit = 15)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
// all categories.
|
|
$result = $this->_queries->journalsByCategory($start, $end);
|
|
$categories = $this->_helper->makeArray($result);
|
|
|
|
// all transfers
|
|
$result = $this->_queries->sharedExpensesByCategory($start, $end);
|
|
$transfers = $this->_helper->makeArray($result);
|
|
$merged = $this->_helper->mergeArrays($categories, $transfers);
|
|
|
|
// sort.
|
|
$sorted = $this->_helper->sortNegativeArray($merged);
|
|
|
|
// limit to $limit:
|
|
$cut = $this->_helper->limitArray($sorted, $limit);
|
|
|
|
return $cut;
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $date
|
|
* @param int $limit
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function getExpenseGroupedForMonth(Carbon $date, $limit = 15)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
|
|
$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) {
|
|
$expenses[-1]['amount'] += $transfer['amount'];
|
|
}
|
|
|
|
$expenses = $this->_helper->sortArray($expenses);
|
|
$limited = $this->_helper->limitArray($expenses, $limit);
|
|
|
|
return $limited;
|
|
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $date
|
|
* @param bool $shared
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function getIncomeForMonth(Carbon $date, $shared = false)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
$userId = $this->_accounts->getUser()->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) {
|
|
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
|
|
}
|
|
)
|
|
->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.*']);
|
|
|
|
// incoming from a shared account: it's profit (income):
|
|
$transfers = \TransactionJournal::withRelevantData()
|
|
->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
|
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
|
->leftJoin(
|
|
'account_meta', function (JoinClause $join) {
|
|
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
|
|
}
|
|
)
|
|
->transactionTypes(['Transfer'])
|
|
->where('transaction_journals.user_id', $userId)
|
|
->where('transactions.amount', '<', 0)
|
|
->where('account_meta.data', '=', '"sharedExpense"')
|
|
->orderBy('date', 'ASC')
|
|
->before($end)->after($start)->get(['transaction_journals.*']);
|
|
|
|
$list = $list->merge($transfers);
|
|
$list->sort(
|
|
function (\TransactionJournal $journal) {
|
|
return $journal->date->format('U');
|
|
}
|
|
);
|
|
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $date
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function getPiggyBanksForMonth(Carbon $date)
|
|
{
|
|
$start = clone $date;
|
|
$start->startOfMonth();
|
|
$end = clone $date;
|
|
$end->endOfMonth();
|
|
|
|
$set = \PiggyBank::
|
|
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
|
|
->where('accounts.user_id', \Auth::user()->id)
|
|
->where('repeats', 0)
|
|
->where(
|
|
function (Builder $query) use ($start, $end) {
|
|
$query->whereNull('piggy_banks.deleted_at');
|
|
$query->orWhere(
|
|
function (Builder $query) use ($start, $end) {
|
|
$query->whereNotNull('piggy_banks.deleted_at');
|
|
$query->where('piggy_banks.deleted_at', '>=', $start->format('Y-m-d 00:00:00'));
|
|
$query->where('piggy_banks.deleted_at', '<=', $end->format('Y-m-d 00:00:00'));
|
|
}
|
|
);
|
|
}
|
|
)
|
|
->get(['piggy_banks.*']);
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $start
|
|
*
|
|
* @return array
|
|
*/
|
|
public function listOfMonths(Carbon $start)
|
|
{
|
|
$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 $start
|
|
*
|
|
* @return array
|
|
*/
|
|
public function listOfYears(Carbon $start)
|
|
{
|
|
$end = Carbon::now();
|
|
$years = [];
|
|
while ($start <= $end) {
|
|
$years[] = $start->format('Y');
|
|
$start->addYear();
|
|
}
|
|
|
|
return $years;
|
|
}
|
|
|
|
/**
|
|
* @param Carbon $start
|
|
* @param Carbon $end
|
|
* @param int $limit
|
|
*
|
|
* @return Collection
|
|
*/
|
|
public function revenueGroupedByAccount(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', 'Deposit')
|
|
->where('acm_to.data', '!=', '"sharedExpense"')
|
|
->before($end)->after($start)
|
|
->where('transaction_journals.user_id', \Auth::user()->id)
|
|
->groupBy('t_from.account_id')->orderBy('sum')->limit(15)
|
|
->get(['t_from.account_id as account_id', 'ac_from.name as name', \DB::Raw('SUM(t_from.amount) as `sum`')]);
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* @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 = $this->_accounts->getAssetAccounts()->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;
|
|
}
|
|
|
|
}
|