diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 41a28b36b6..eb69f95549 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -141,8 +141,6 @@ class CategoryController extends Controller { /** @var CRI $repository */ $repository = app('FireflyIII\Repositories\Category\CategoryRepositoryInterface'); - /** @var SCRI $singleRepository */ - $singleRepository = app('FireflyIII\Repositories\Category\SingleCategoryRepositoryInterface'); // chart properties for cache: $cache = new CacheProperties(); @@ -167,7 +165,14 @@ class CategoryController extends Controller * earned: x */ $entries = new Collection; - // go by budget, not by year. + // go by category, not by year. + + // given a set of categories and accounts, it should not be difficult to get + // the exact array of data we need. + + // then get the data for "no category". + $set = $repository->listMultiYear($categories, $accounts, $start, $end); + /** @var Category $category */ foreach ($categories as $category) { $entry = ['name' => '', 'spent' => [], 'earned' => []]; @@ -175,22 +180,35 @@ class CategoryController extends Controller $currentStart = clone $start; while ($currentStart < $end) { // fix the date: + $year = $currentStart->year; $currentEnd = clone $currentStart; $currentEnd->endOfYear(); + // get data: if (is_null($category->id)) { $name = trans('firefly.noCategory'); $spent = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd); $earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd); } else { + // get from set: + $entrySpent = $set->filter( + function (Category $cat) use ($year, $category) { + return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id); + } + )->first(); + $entryEarned = $set->filter( + function (Category $cat) use ($year, $category) { + return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id); + } + )->first(); + $name = $category->name; - $spent = $singleRepository->spentInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); - $earned = $singleRepository->earnedInPeriodForAccounts($category, $accounts, $currentStart, $currentEnd); + $spent = !is_null($entrySpent) ? $entrySpent->sum : 0; + $earned = !is_null($entryEarned) ? $entryEarned->sum : 0; } // save to array: - $year = $currentStart->year; $entry['name'] = $name; $entry['spent'][$year] = ($spent * -1); $entry['earned'][$year] = $earned; @@ -205,7 +223,6 @@ class CategoryController extends Controller $data = $this->generator->multiYear($entries); $cache->store($data); - return Response::json($data); } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 5e1deb4636..bd5e0e83b8 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -122,6 +122,62 @@ class CategoryRepository implements CategoryRepositoryInterface ->get(['transaction_journals.*']); } + /** + * This method returns a very special collection for each category: + * + * category, year, expense/earned, amount + * + * categories can be duplicated. + * + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end) + { + /* + * select categories.id, DATE_FORMAT(transaction_journals.date,"%Y") as dateFormatted, transaction_types.type, SUM(amount) as sum from categories + +left join category_transaction_journal ON category_transaction_journal.category_id = categories.id +left join transaction_journals ON transaction_journals.id = category_transaction_journal.transaction_journal_id +left join transaction_types ON transaction_types.id = transaction_journals.transaction_type_id +left join transactions ON transactions.transaction_journal_id = transaction_journals.id + + +where +categories.user_id =1 +and transaction_types.type in ("Withdrawal","Deposit") +and transactions.account_id IN (2,4,6,10,11,610,725,879,1248) + +group by categories.id, transaction_types.type, dateFormatted + */ + $set = Auth::user()->categories() + ->leftJoin('category_transaction_journal', 'category_transaction_journal.category_id', '=', 'categories.id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'category_transaction_journal.transaction_journal_id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereIn('transaction_types.type', [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL]) + ->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) + ->whereIn('categories.id', $categories->pluck('id')->toArray()) + ->groupBy('categories.id') + ->groupBy('transaction_types.type') + ->groupBy('dateFormatted') + ->get( + [ + 'categories.*', + DB::Raw('DATE_FORMAT(`transaction_journals`.`date`,"%Y") as `dateFormatted`'), + 'transaction_types.type', + DB::Raw('SUM(`amount`) as `sum`') + ] + ); + + return $set; + + } + /** * Returns a collection of Categories appended with the amount of money that has been earned @@ -136,11 +192,6 @@ class CategoryRepository implements CategoryRepositoryInterface */ 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') @@ -156,8 +207,8 @@ class CategoryRepository implements CategoryRepositoryInterface $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('t_dest.account_id', $accounts->pluck('id')->toArray())// to these accounts (earned) + ->whereNotIn('t_src.account_id', $accounts->pluck('id')->toArray())//-- but not from these accounts ->whereIn( 'transaction_types.type', [TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE] ) @@ -191,12 +242,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function spentForAccountsPerMonth(Collection $accounts, Carbon $start, Carbon $end) { - $accountIds = []; - foreach ($accounts as $account) { - $accountIds[] = $account->id; - } - - + $accountIds = $accounts->pluck('id')->toArray(); $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') @@ -275,10 +321,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ protected function sumNoCategory(Collection $accounts, Carbon $start, Carbon $end, $group = Query::EARNED) { - $accountIds = []; - foreach ($accounts as $account) { - $accountIds[] = $account->id; - } + $accountIds = $accounts->pluck('id')->toArray(); if ($group == Query::EARNED) { $types = [TransactionType::DEPOSIT]; } else { diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 2bf6825beb..47bba21750 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -3,7 +3,6 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; -use FireflyIII\Sql\Query; use Illuminate\Support\Collection; /** @@ -45,6 +44,22 @@ interface CategoryRepositoryInterface */ public function listCategories(); + /** + * This method returns a very special collection for each category: + * + * category, year, expense/earned, amount + * + * categories can be duplicated. + * + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function listMultiYear(Collection $categories, Collection $accounts, Carbon $start, Carbon $end); + /** * Returns a list of transaction journals in the range (all types, all accounts) that have no category * associated to them.