First attempt at optimised query for multi-year budget chart.

This commit is contained in:
James Cole 2015-12-28 19:56:28 +01:00
parent 83de3482ce
commit 6ab6dd6ac3
3 changed files with 194 additions and 49 deletions

View File

@ -37,6 +37,8 @@ class BudgetController extends Controller
} }
/** /**
* TODO expand with no budget chart.
*
* @param BudgetRepositoryInterface $repository * @param BudgetRepositoryInterface $repository
* @param $report_type * @param $report_type
* @param Carbon $start * @param Carbon $start
@ -61,41 +63,43 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
/** /*
* budget * Get the budgeted amounts for each budgets in each year.
* year:
* spent: x
* budgeted: x
* year
* spent: x
* budgeted: x
*/ */
$budgetedSet = $repository->getBudgetedPerYear($budgets, $start, $end);
$budgetedArray = [];
/** @var Budget $entry */
foreach ($budgetedSet as $entry) {
$budgetedArray[$entry->id][$entry->dateFormatted] = $entry->budgeted;
}
$set = $repository->getBudgetsAndExpensesPerYear($budgets, $accounts, $start, $end);
$entries = new Collection; $entries = new Collection;
// go by budget, not by year. // go by budget, not by year.
/** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
$entry = ['name' => '', 'spent' => [], 'budgeted' => []]; $entry = ['name' => '', 'spent' => [], 'budgeted' => []];
$id = $budget->id;
$currentStart = clone $start; $currentStart = clone $start;
while ($currentStart < $end) { while ($currentStart < $end) {
// fix the date: // fix the date:
$currentEnd = clone $currentStart; $currentEnd = clone $currentStart;
$currentEnd->endOfYear(); $currentEnd->endOfYear();
// get data: // save to array:
if (is_null($budget->id)) { $year = $currentStart->year;
$name = trans('firefly.noBudget'); $entry['name'] = $budget->name;
$sum = $repository->getWithoutBudgetSum($currentStart, $currentEnd); $spent = 0;
$budgeted = 0; $budgeted = 0;
} else { if (isset($set[$id]['entries'][$year])) {
$name = $budget->name; $spent = $set[$id]['entries'][$year] * -1;
$sum = $repository->balanceInPeriod($budget, $currentStart, $currentEnd, $accounts);
$budgeted = $repository->getBudgetLimitRepetitions($budget, $currentStart, $currentEnd)->sum('amount');
} }
// save to array: if (isset($budgetedArray[$id][$year])) {
$year = $currentStart->year; $budgeted = round($budgetedArray[$id][$year], 2);
$entry['name'] = $name; }
$entry['spent'][$year] = ($sum * -1);
$entry['spent'][$year] = $spent;
$entry['budgeted'][$year] = $budgeted; $entry['budgeted'][$year] = $budgeted;
// jump to next year. // jump to next year.
@ -106,6 +110,7 @@ class BudgetController extends Controller
} }
// generate chart with data: // generate chart with data:
$data = $this->generator->multiYear($entries); $data = $this->generator->multiYear($entries);
$cache->store($data);
return Response::json($data); return Response::json($data);
@ -273,6 +278,8 @@ class BudgetController extends Controller
} }
/** /**
* TODO expand with no budget chart.
*
* @param BudgetRepositoryInterface $repository * @param BudgetRepositoryInterface $repository
* @param $report_type * @param $report_type
* @param Carbon $start * @param Carbon $start
@ -295,7 +302,7 @@ class BudgetController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$budgetInformation = $repository->getBudgetsAndExpenses($start, $end); $budgetInformation = $repository->getBudgetsAndExpensesPerMonth($accounts, $start, $end);
$budgets = new Collection; $budgets = new Collection;
$entries = new Collection; $entries = new Collection;

View File

@ -5,6 +5,7 @@ namespace FireflyIII\Repositories\Budget;
use Auth; use Auth;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\LimitRepetition;
@ -214,33 +215,41 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
* Returns an array with every budget in it and the expenses for each budget * Returns an array with every budget in it and the expenses for each budget
* per month. * per month.
* *
* @param Carbon $start * @param Collection $accounts
* @param Carbon $end * @param Carbon $start
* @param Carbon $end
* *
* @return array * @return array
*/ */
public function getBudgetsAndExpenses(Carbon $start, Carbon $end) public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end)
{ {
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
/** @var Collection $set */ /** @var Collection $set */
$set = Auth::user()->budgets() $set = Auth::user()->budgets()
->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->leftJoin( ->leftJoin(
'transactions', function (JoinClause $join) { 'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
} }
) )
->groupBy('budgets.id') ->groupBy('budgets.id')
->groupBy('dateFormatted') ->groupBy('dateFormatted')
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->get( ->whereIn('transactions.account_id', $ids)
[ ->get(
'budgets.*', [
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), 'budgets.*',
DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`') DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'),
] DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`')
); ]
);
$set = $set->sortBy( $set = $set->sortBy(
function (Budget $budget) { function (Budget $budget) {
@ -260,6 +269,7 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
// store each entry: // store each entry:
$return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount;
} }
return $return; return $return;
} }
@ -503,7 +513,110 @@ class BudgetRepository extends ComponentRepository implements BudgetRepositoryIn
} }
return $limit; return $limit;
}
/**
* Get the budgeted amounts for each budgets in each year.
*
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end)
{
$budgetIds = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
$budgetIds[] = $budget->id;
}
$set = Auth::user()->budgets()
->leftJoin('budget_limits', 'budgets.id', '=', 'budget_limits.budget_id')
->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d'))
->where('limit_repetitions.enddate', '<=', $end->format('Y-m-d'))
->groupBy('budgets.id')
->groupBy('dateFormatted')
->whereIn('budgets.id', $budgetIds)
->get(
[
'budgets.*',
DB::Raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'),
DB::Raw('SUM(`limit_repetitions`.`amount`) as `budgeted`')
]
);
return $set;
}
/**
* Returns an array with every budget in it and the expenses for each budget
* per year for.
*
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end)
{
$ids = [];
/** @var Account $account */
foreach ($accounts as $account) {
$ids[] = $account->id;
}
$budgetIds = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
$budgetIds[] = $budget->id;
}
/** @var Collection $set */
$set = Auth::user()->budgets()
->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id')
->leftJoin(
'transactions', function (JoinClause $join) {
$join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0);
}
)
->groupBy('budgets.id')
->groupBy('dateFormatted')
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->whereIn('transactions.account_id', $ids)
->whereIn('budgets.id', $budgetIds)
->get(
[
'budgets.*',
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'),
DB::Raw('SUM(`transactions`.`amount`) AS `sumAmount`')
]
);
$set = $set->sortBy(
function (Budget $budget) {
return strtolower($budget->name);
}
);
$return = [];
foreach ($set as $budget) {
$id = $budget->id;
if (!isset($return[$id])) {
$return[$id] = [
'budget' => $budget,
'entries' => [],
];
}
// store each entry:
$return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount;
}
return $return;
} }
} }

View File

@ -31,12 +31,26 @@ interface BudgetRepositoryInterface
* Returns an array with every budget in it and the expenses for each budget * Returns an array with every budget in it and the expenses for each budget
* per month. * per month.
* *
* @param Carbon $start * @param Collection $accounts
* @param Carbon $end * @param Carbon $start
* @param Carbon $end
* *
* @return array * @return array
*/ */
public function getBudgetsAndExpenses(Carbon $start, Carbon $end); public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns an array with every budget in it and the expenses for each budget
* per year for.
*
* @param Collection $budgets
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end);
/** /**
* Takes tags into account. * Takes tags into account.
@ -53,6 +67,17 @@ interface BudgetRepositoryInterface
*/ */
public function getActiveBudgets(); public function getActiveBudgets();
/**
* Get the budgeted amounts for each budgets in each year.
*
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end);
/** /**
* @param Budget $budget * @param Budget $budget
* @param Carbon $start * @param Carbon $start