Another chart optimised

This commit is contained in:
James Cole 2015-12-29 18:55:30 +01:00
parent 0fd0d7d080
commit 35154dc7a3
3 changed files with 172 additions and 95 deletions

View File

@ -304,7 +304,6 @@ class CategoryController extends Controller
*/ */
public function earnedInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) public function earnedInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{ {
$original = clone $start;
$cache = new CacheProperties; // chart properties for cache: $cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@ -315,78 +314,42 @@ class CategoryController extends Controller
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
// run a very special query each month: $set = $repository->earnedForAccountsPerMonth($accounts, $start, $end);
$start = clone $original; $categories = $set->unique('id')->sortBy(
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
// get a list of categories, and what the user has earned for that category
// (if the user has earned anything)
$set = $repository->earnedForAccounts($accounts, $currentStart, $currentEnd);
$categories = $categories->merge($set);
// save the set combined with the data that is in it:
// for example:
// december 2015, salary:1000, bonus:200
$sets->push([$currentStart, $set]);
$start->addMonth();
}
// filter categories into a single bunch. Useful later on.
// $categories contains all the categories the user has earned money
// in in this period.
$categories = $categories->unique('id');
$categories = $categories->sortBy(
function (Category $category) { function (Category $category) {
return $category->name; return $category->name;
} }
); );
$entries = new Collection;
// start looping the time again, this time processing the while ($start < $end) { // filter the set:
// data for each month. $row = [clone $start];
$start = clone $original; // get possibly relevant entries from the big $set
while ($start < $end) { $currentSet = $set->filter(
$currentEnd = clone $start; function (Category $category) use ($start) {
$currentStart = clone $start; return $category->dateFormatted == $start->format("Y-m");
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
// in $sets we have saved all the sets of data for each month
// so now we need to retrieve the corrent one.
// match is on date of course.
$currentSet = $sets->first(
function ($key, $value) use ($currentStart) {
// set for this date.
return ($value[0] == $currentStart);
} }
); );
// create a row used later on. // check for each category if its in the current set.
$row = [clone $currentStart];
// loop all categories:
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
// if entry is not null, we've earned money in this period for this category. // if its in there, use the value.
$entry = $currentSet[1]->first( $entry = $currentSet->filter(
function ($key, $value) use ($category) { function (Category $cat) use ($category) {
return $value->id == $category->id; return ($cat->id == $category->id);
} }
); )->first();
// save amount
if (!is_null($entry)) { if (!is_null($entry)) {
$row[] = $entry->earned; $row[] = round($entry->earned, 2);
} else { } else {
$row[] = 0; $row[] = 0;
} }
} }
$entries->push($row); $entries->push($row);
$start->addMonth(); $start->addMonth();
} }
$data = $this->generator->earnedInPeriod($categories, $entries); $data = $this->generator->earnedInPeriod($categories, $entries);
$cache->store($data); $cache->store($data);
@ -408,7 +371,6 @@ class CategoryController extends Controller
*/ */
public function spentInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) public function spentInPeriod(CategoryRepositoryInterface $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts)
{ {
$original = clone $start;
$cache = new CacheProperties; // chart properties for cache: $cache = new CacheProperties; // chart properties for cache:
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@ -419,66 +381,46 @@ class CategoryController extends Controller
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$categories = new Collection;
$sets = new Collection;
$entries = new Collection;
// run a very special query each month: $set = $repository->spentForAccountsPerMonth($accounts, $start, $end);
$start = clone $original; $categories = $set->unique('id')->sortBy(
while ($start < $end) {
$currentEnd = clone $start;
$currentStart = clone $start;
$currentStart->startOfMonth();
$currentEnd->endOfMonth();
$set = $repository->spentForAccounts($accounts, $currentStart, $currentEnd);
$categories = $categories->merge($set);
$sets->push([$currentStart, $set]);
$start->addMonth();
}
$categories = $categories->unique('id');
$categories = $categories->sortBy(
function (Category $category) { function (Category $category) {
return $category->name; return $category->name;
} }
); );
$entries = new Collection;
$start = clone $original; while ($start < $end) { // filter the set:
while ($start < $end) { $row = [clone $start];
$currentEnd = clone $start; // get possibly relevant entries from the big $set
$currentStart = clone $start; $currentSet = $set->filter(
$currentStart->startOfMonth(); function (Category $category) use ($start) {
$currentEnd->endOfMonth(); return $category->dateFormatted == $start->format("Y-m");
$currentSet = $sets->first(
function ($key, $value) use ($currentStart) {
// set for this date.
return ($value[0] == $currentStart);
} }
); );
$row = [clone $currentStart]; // check for each category if its in the current set.
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
/** @var Category $entry */ // if its in there, use the value.
$entry = $currentSet[1]->first( $entry = $currentSet->filter(
function ($key, $value) use ($category) { function (Category $cat) use ($category) {
return $value->id == $category->id; return ($cat->id == $category->id);
} }
); )->first();
if (!is_null($entry)) { if (!is_null($entry)) {
$row[] = $entry->spent; $row[] = round(($entry->spent * -1), 2);
} else { } else {
$row[] = 0; $row[] = 0;
} }
} }
$entries->push($row); $entries->push($row);
$start->addMonth(); $start->addMonth();
} }
$data = $this->generator->spentInPeriod($categories, $entries); $data = $this->generator->spentInPeriod($categories, $entries);
$cache->store($data); $cache->store($data);
return $data; return $data;
} }
} }

View File

@ -531,6 +531,61 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
} }
/**
* Returns a collection of Categories appended with the amount of money that has been earned
* in these categories, based on the $accounts involved, in period X, grouped per month.
* The amount earned in category X in period X is saved in field "earned".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$collection = Auth::user()->categories()
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions AS t_src', function (JoinClause $join) {
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_dest', function (JoinClause $join) {
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
}
)
->whereIn('t_dest.account_id', $accountIds)// to these accounts (earned)
->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts
->whereIn(
'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
)
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('categories.id')
->groupBy('dateFormatted')
->get(
[
'categories.*',
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'),
DB::Raw('SUM(`t_dest`.`amount`) AS `earned`')
]
);
return $collection;
}
/** /**
* Returns a collection of Categories appended with the amount of money that has been earned * Returns a collection of Categories appended with the amount of money that has been earned
* in these categories, based on the $accounts involved, in period X. * in these categories, based on the $accounts involved, in period X.
@ -568,7 +623,7 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts ->whereNotIn('t_src.account_id', $accountIds)//-- but not from these accounts
->whereIn( ->whereIn(
'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE] 'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
)// earned from these things. )
->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')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('categories.id') ->groupBy('categories.id')
@ -624,4 +679,58 @@ class CategoryRepository extends ComponentRepository implements CategoryReposito
return $collection; return $collection;
} }
/**
* Returns a collection of Categories appended with the amount of money that has been spent
* in these categories, based on the $accounts involved, in period X, grouped per month.
* The amount earned in category X in period X is saved in field "spent".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end)
{
$accountIds = [];
foreach ($accounts as $account) {
$accountIds[] = $account->id;
}
$collection = Auth::user()->categories()
->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id')
->leftJoin('transaction_journals', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin(
'transactions AS t_src', function (JoinClause $join) {
$join->on('t_src.transaction_journal_id', '=', 'transaction_journals.id')->where('t_src.amount', '<', 0);
}
)
->leftJoin(
'transactions AS t_dest', function (JoinClause $join) {
$join->on('t_dest.transaction_journal_id', '=', 'transaction_journals.id')->where('t_dest.amount', '>', 0);
}
)
->whereIn('t_src.account_id', $accountIds)// from these accounts (spent)
->whereNotIn('t_dest.account_id', $accountIds)//-- but not from these accounts (spent internally)
->whereIn(
'transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE]
)// spent on these things.
->where('transaction_journals.date', '>=', $start->format('Y-m-d'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d'))
->groupBy('categories.id')
->groupBy('dateFormatted')
->get(
[
'categories.*',
DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y-%m") as `dateFormatted`'),
DB::Raw('SUM(`t_src`.`amount`) AS `spent`')
]
);
return $collection;
}
} }

View File

@ -63,6 +63,32 @@ interface CategoryRepositoryInterface
*/ */
public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end); public function spentForAccounts(Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns a collection of Categories appended with the amount of money that has been spent
* in these categories, based on the $accounts involved, in period X, grouped per month.
* The amount earned in category X in period X is saved in field "spent".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end);
/**
* Returns a collection of Categories appended with the amount of money that has been earned
* in these categories, based on the $accounts involved, in period X, grouped per month.
* The amount earned in category X in period X is saved in field "earned".
*
* @param $accounts
* @param $start
* @param $end
*
* @return Collection
*/
public function earnedForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end);
/** /**
* @return Collection * @return Collection
*/ */