diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index 6bdf2f8f8a..8f6a572c47 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -33,6 +33,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\PeriodOverview; use FireflyIII\Support\Http\Controllers\UserNavigation; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -45,7 +46,7 @@ use View; */ class ShowController extends Controller { - use UserNavigation; + use UserNavigation, PeriodOverview; /** @var CurrencyRepositoryInterface The currency repository */ private $currencyRepos; @@ -114,7 +115,7 @@ class ShowController extends Controller $fEnd = $end->formatLocalized($this->monthAndDayFormat); $subTitle = (string)trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]); $chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]); - $periods = $this->getPeriodOverview($account, $end); + $periods = $this->getAccountPeriodOverview($account, $end); /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page); @@ -172,76 +173,4 @@ class ShowController extends Controller ); } - - - /** @noinspection MoreThanThreeArgumentsInspection */ - - /** - * This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range) - * and for each period, the amount of money spent and earned. This is a complex operation which is cached for - * performance reasons. - * - * @param Account $account the account involved - * - * @param Carbon|null $date - * - * @return Collection - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function getPeriodOverview(Account $account, ?Carbon $date): Collection // period overview - { - $range = app('preferences')->get('viewRange', '1M')->data; - $start = $this->repository->oldestJournalDate($account) ?? Carbon::now()->startOfMonth(); - $end = $date ?? new Carbon; - if ($end < $start) { - [$start, $end] = [$end, $start]; // @codeCoverageIgnore - } - - // properties for cache - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('account-show-period-entries'); - $cache->addProperty($account->id); - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - /** @var array $dates */ - $dates = app('navigation')->blockPeriods($start, $end, $range); - $entries = new Collection; - // loop dates - foreach ($dates as $currentDate) { - - // try a collector for income: - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::DEPOSIT]) - ->withOpposingAccount(); - $earned = (string)$collector->getJournals()->sum('transaction_amount'); - - // try a collector for expenses: - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::WITHDRAWAL]) - ->withOpposingAccount(); - $spent = (string)$collector->getJournals()->sum('transaction_amount'); - - $dateName = app('navigation')->periodShow($currentDate['start'], $currentDate['period']); - /** @noinspection PhpUndefinedMethodInspection */ - $entries->push( - [ - 'name' => $dateName, - 'spent' => $spent, - 'earned' => $earned, - 'start' => $currentDate['start']->format('Y-m-d'), - 'end' => $currentDate['end']->format('Y-m-d'), - ] - ); - } - - $cache->store($entries); - - return $entries; - } } diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index cf385a8771..8aee78d8b7 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -43,6 +44,7 @@ use Illuminate\Support\Collection; */ class ShowController extends Controller { + use PeriodOverview; /** @var BudgetRepositoryInterface The budget repository */ private $repository; @@ -86,7 +88,7 @@ class ShowController extends Controller 'firefly.without_budget_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ); - $periods = $this->getPeriodOverview(); + $periods = $this->getBudgetPeriodOverview(); $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; @@ -236,57 +238,4 @@ class ShowController extends Controller return $set; } - - - /** - * Gets period overview used for budgets. - * - * @return Collection - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function getPeriodOverview(): Collection - { - /** @var JournalRepositoryInterface $repository */ - $repository = app(JournalRepositoryInterface::class); - $first = $repository->firstNull(); - $start = null === $first ? new Carbon : $first->date; - $range = app('preferences')->get('viewRange', '1M')->data; - $start = app('navigation')->startOfPeriod($start, $range); - $end = app('navigation')->endOfX(new Carbon, $range, null); - $entries = new Collection; - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('no-budget-period-entries'); - - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - $dates = app('navigation')->blockPeriods($start, $end, $range); - foreach ($dates as $date) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes( - [TransactionType::WITHDRAWAL] - ); - $set = $collector->getJournals(); - $sum = (string)($set->sum('transaction_amount') ?? '0'); - $journals = $set->count(); - /** @noinspection PhpUndefinedMethodInspection */ - $dateStr = $date['end']->format('Y-m-d'); - $dateName = app('navigation')->periodShow($date['end'], $date['period']); - $entries->push( - ['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end'], - 'start' => $date['start'], - 'end' => $date['end'], - - ] - ); - } - $cache->store($entries); - - return $entries; - } - } diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index bc03dc6f22..e226c943ee 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -27,13 +27,11 @@ use Carbon\Carbon; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Http\Controllers\Controller; -use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; -use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Log; @@ -46,7 +44,7 @@ use Log; */ class ShowController extends Controller { - + use PeriodOverview; /** @var AccountRepositoryInterface The account repository */ private $accountRepos; /** @var JournalRepositoryInterface Journals and transactions overview */ @@ -96,7 +94,7 @@ class ShowController extends Controller $subTitleIcon = 'fa-bar-chart'; $page = (int)$request->get('page'); $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; - $periods = $this->getPeriodOverview($category, $start); + $periods = $this->getCategoryPeriodOverview($category, $start); $path = route('categories.show', [$category->id, $start->format('Y-m-d'), $end->format('Y-m-d')]); $subTitle = trans( 'firefly.journals_in_period_for_category', @@ -153,72 +151,4 @@ class ShowController extends Controller return view('categories.show', compact('category', 'transactions', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); } - - /** - * Get a period overview for category. - * - * @param Category $category - * - * @param Carbon $date - * - * @return Collection - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function getPeriodOverview(Category $category, Carbon $date): Collection // periodOverview method - { - $range = app('preferences')->get('viewRange', '1M')->data; - $first = $this->journalRepos->firstNull(); - $start = null === $first ? new Carbon : $first->date; - $end = $date ?? new Carbon; - $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - - // properties for entries with their amounts. - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($range); - $cache->addProperty('categories.entries'); - $cache->addProperty($category->id); - - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - /** @var array $dates */ - $dates = app('navigation')->blockPeriods($start, $end, $range); - $entries = new Collection; - - foreach ($dates as $currentDate) { - $spent = $this->repository->spentInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']); - $earned = $this->repository->earnedInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']); - /** @noinspection PhpUndefinedMethodInspection */ - $dateStr = $currentDate['end']->format('Y-m-d'); - $dateName = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); - - // amount transferred - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setAllAssetAccounts()->setRange($currentDate['start'], $currentDate['end'])->setCategory($category) - ->withOpposingAccount()->setTypes([TransactionType::TRANSFER]); - $collector->removeFilter(InternalTransferFilter::class); - $transferred = app('steam')->positive((string)$collector->getJournals()->sum('transaction_amount')); - - $entries->push( - [ - 'string' => $dateStr, - 'name' => $dateName, - 'spent' => $spent, - 'earned' => $earned, - 'sum' => bcadd($earned, $spent), - 'transferred' => $transferred, - 'start' => clone $currentDate['start'], - 'end' => clone $currentDate['end'], - ] - ); - } - $cache->store($entries); - - return $entries; - } - } diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 9bce231b13..024b5a0835 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -32,10 +32,9 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -50,7 +49,7 @@ use Log; */ class AccountController extends Controller { - use DateCalculation; + use DateCalculation, AugumentData; /** @var GeneratorInterface Chart generation methods. */ protected $generator; @@ -508,51 +507,4 @@ class AccountController extends Controller return $data; } - /** - * Get the budget names from a set of budget ID's. - * - * @param array $budgetIds - * - * @return array - */ - protected function getBudgetNames(array $budgetIds): array // extract info from array. - { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - $budgets = $repository->getBudgets(); - $grouped = $budgets->groupBy('id')->toArray(); - $return = []; - foreach ($budgetIds as $budgetId) { - if (isset($grouped[$budgetId])) { - $return[$budgetId] = $grouped[$budgetId][0]['name']; - } - } - $return[0] = (string)trans('firefly.no_budget'); - - return $return; - } - - /** - * Get the category names from a set of category ID's. Small helper function for some of the charts. - * - * @param array $categoryIds - * - * @return array - */ - protected function getCategoryNames(array $categoryIds): array // extract info from array. - { - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - $categories = $repository->getCategories(); - $grouped = $categories->groupBy('id')->toArray(); - $return = []; - foreach ($categoryIds as $categoryId) { - if (isset($grouped[$categoryId])) { - $return[$categoryId] = $grouped[$categoryId][0]['name']; - } - } - $return[0] = (string)trans('firefly.noCategory'); - - return $return; - } } diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index fde4d49516..5173ebc8da 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -27,15 +27,14 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; -use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\DateCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -49,7 +48,7 @@ use Illuminate\Support\Collection; */ class BudgetController extends Controller { - use DateCalculation; + use DateCalculation, AugumentData; /** @var GeneratorInterface Chart generation methods. */ protected $generator; @@ -461,29 +460,6 @@ class BudgetController extends Controller return response()->json($data); } - /** - * Get the account names belonging to a bunch of account ID's. - * - * @param array $accountIds - * - * @return array - */ - protected function getAccountNames(array $accountIds): array // extract info from array. - { - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::CASH]); - $grouped = $accounts->groupBy('id')->toArray(); - $return = []; - foreach ($accountIds as $accountId) { - if (isset($grouped[$accountId])) { - $return[$accountId] = $grouped[$accountId][0]['name']; - } - } - $return[0] = '(no name)'; - - return $return; - } /** * Get the amount of money budgeted in a period. @@ -515,29 +491,7 @@ class BudgetController extends Controller return $budgeted; } - /** - * Small helper function for some of the charts. Extracts category names from a bunch of ID's. - * - * @param array $categoryIds - * - * @return array - */ - protected function getCategoryNames(array $categoryIds): array // extract info from arrat - { - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - $categories = $repository->getCategories(); - $grouped = $categories->groupBy('id')->toArray(); - $return = []; - foreach ($categoryIds as $categoryId) { - if (isset($grouped[$categoryId])) { - $return[$categoryId] = $grouped[$categoryId][0]['name']; - } - } - $return[0] = (string)trans('firefly.noCategory'); - return $return; - } /** @noinspection MoreThanThreeArgumentsInspection */ /** diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index 65bab5efe3..de8da30cfd 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -31,11 +31,11 @@ use FireflyIII\Helpers\Filter\PositiveAmountFilter; use FireflyIII\Helpers\Filter\TransferFilter; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; -use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\AugumentData; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -48,6 +48,7 @@ use Illuminate\Support\Collection; */ class BudgetReportController extends Controller { + use AugumentData; /** @var BudgetRepositoryInterface The budget repository */ private $budgetRepository; /** @var GeneratorInterface Chart generation methods. */ @@ -217,34 +218,6 @@ class BudgetReportController extends Controller return response()->json($data); } - /** @noinspection MoreThanThreeArgumentsInspection */ - /** - * Returns the budget limits belonging to the given budget and valid on the given day. - * - * @param Collection $budgetLimits - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - protected function filterBudgetLimits(Collection $budgetLimits, Budget $budget, Carbon $start, Carbon $end): Collection // filter data - { - $set = $budgetLimits->filter( - function (BudgetLimit $budgetLimit) use ($budget, $start, $end) { - if ($budgetLimit->budget_id === $budget->id - && $budgetLimit->start_date->lte($start) // start of budget limit is on or before start - && $budgetLimit->end_date->gte($end) // end of budget limit is on or after end - ) { - return $budgetLimit; - } - - return false; - } - ); - - return $set; - } /** @noinspection MoreThanThreeArgumentsInspection */ /** diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 2209ab707c..3eddbc2839 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -27,6 +27,7 @@ use FireflyIII\Helpers\Report\BudgetReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\BasicDataSupport; use Illuminate\Support\Collection; use Log; use Throwable; @@ -36,6 +37,7 @@ use Throwable; */ class BudgetController extends Controller { + use BasicDataSupport; /** * Show partial overview of budgets. @@ -97,7 +99,7 @@ class BudgetController extends Controller $budgets = $repository->getBudgets(); $data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end); $data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget" - $report = $this->filterBudgetPeriodReport($data); + $report = $this->filterPeriodReport($data); $periods = app('navigation')->listOfPeriods($start, $end); try { $result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); @@ -110,30 +112,4 @@ class BudgetController extends Controller return $result; } - /** - * Filters empty results from getBudgetPeriodReport. - * - * @param array $data - * - * @return array - */ - protected function filterBudgetPeriodReport(array $data): array // helper function for period overview. - { - /** - * @var int - * @var array $set - */ - foreach ($data as $budgetId => $set) { - $sum = '0'; - foreach ($set['entries'] as $amount) { - $sum = bcadd($amount, $sum); - } - $data[$budgetId]['sum'] = $sum; - if (0 === bccomp('0', $sum)) { - unset($data[$budgetId]); - } - } - - return $data; - } } diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index 6337f92fde..14dc4cf999 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -27,6 +27,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\BasicDataSupport; use Illuminate\Support\Collection; use Log; use Throwable; @@ -36,6 +37,7 @@ use Throwable; */ class CategoryController extends Controller { + use BasicDataSupport; /** * Show overview of expenses in category. @@ -101,7 +103,7 @@ class CategoryController extends Controller $categories = $repository->getCategories(); $data = $repository->periodIncome($categories, $accounts, $start, $end); $data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end); - $report = $this->filterReport($data); + $report = $this->filterPeriodReport($data); $periods = app('navigation')->listOfPeriods($start, $end); try { $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); @@ -166,26 +168,5 @@ class CategoryController extends Controller return $result; } - /** - * Filters empty results from category period report. - * - * @param array $data - * - * @return array - */ - protected function filterReport(array $data): array // filter data from report. - { - foreach ($data as $categoryId => $set) { - $sum = '0'; - foreach ($set['entries'] as $amount) { - $sum = bcadd($amount, $sum); - } - $data[$categoryId]['sum'] = $sum; - if (0 === bccomp('0', $sum)) { - unset($data[$categoryId]); - } - } - return $data; - } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 14cc0d01d2..615249cc62 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -30,6 +30,7 @@ use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; +use FireflyIII\Support\Http\Controllers\PeriodOverview; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -39,6 +40,8 @@ use Illuminate\Support\Collection; */ class TagController extends Controller { + use PeriodOverview; + /** @var TagRepositoryInterface The tag repository. */ protected $repository; @@ -190,7 +193,7 @@ class TagController extends Controller 'firefly.journals_in_period_for_tag', ['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),] ); - $periods = $this->getPeriodOverview($tag); + $periods = $this->getTagPeriodOverview($tag); $path = route('tags.show', [$tag->id, $start->format('Y-m-d'), $end->format('Y-m-d')]); /** @var JournalCollectorInterface $collector */ @@ -295,59 +298,5 @@ class TagController extends Controller return $redirect; } - /** - * Get overview of periods for tag. - * - * @param Tag $tag - * - * @return Collection - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function getPeriodOverview(Tag $tag): Collection // period overview for tags. - { - // get first and last tag date from tag: - $range = app('preferences')->get('viewRange', '1M')->data; - /** @var Carbon $end */ - $end = app('navigation')->endOfX($this->repository->lastUseDate($tag) ?? new Carbon, $range, null); - $start = $this->repository->firstUseDate($tag) ?? new Carbon; - - // properties for entries with their amounts. - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('tag.entries'); - $cache->addProperty($tag->id); - - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - - $collection = new Collection; - $currentEnd = clone $end; - // while end larger or equal to start - while ($currentEnd >= $start) { - $currentStart = app('navigation')->startOfPeriod($currentEnd, $range); - - // get expenses and what-not in this period and this tag. - $arr = [ - 'string' => $end->format('Y-m-d'), - 'name' => app('navigation')->periodShow($currentEnd, $range), - 'start' => clone $currentStart, - 'end' => clone $currentEnd, - 'date' => clone $end, - 'spent' => $this->repository->spentInPeriod($tag, $currentStart, $currentEnd), - 'earned' => $this->repository->earnedInPeriod($tag, $currentStart, $currentEnd), - ]; - $collection->push($arr); - - /** @var Carbon $currentEnd */ - $currentEnd = clone $currentStart; - $currentEnd->subDay(); - } - $cache->store($collection); - - return $collection; - } } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index fbecc45ada..96fea54c56 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -36,6 +36,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Http\Controllers\ModelInformation; +use FireflyIII\Support\Http\Controllers\PeriodOverview; use FireflyIII\Transformers\TransactionTransformer; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -51,7 +52,7 @@ use View; */ class TransactionController extends Controller { - use ModelInformation; + use ModelInformation, PeriodOverview; /** @var JournalRepositoryInterface Journals and transactions overview */ private $repository; @@ -106,7 +107,7 @@ class TransactionController extends Controller $startStr = $start->formatLocalized($this->monthAndDayFormat); $endStr = $end->formatLocalized($this->monthAndDayFormat); $subTitle = (string)trans('firefly.title_' . $what . '_between', ['start' => $startStr, 'end' => $endStr]); - $periods = $this->getPeriodOverview($what, $end); + $periods = $this->getTransactionPeriodOverview($what, $end); /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); @@ -248,98 +249,5 @@ class TransactionController extends Controller return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'linkTypes', 'links')); } - /** - * Get period overview for index. - * - * @param string $what - * - * @param Carbon $date - * - * @return Collection - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - protected function getPeriodOverview(string $what, Carbon $date): Collection // period overview for transactions. - { - $range = app('preferences')->get('viewRange', '1M')->data; - $first = $this->repository->firstNull(); - $start = Carbon::now()->subYear(); - $types = config('firefly.transactionTypesByWhat.' . $what); - $entries = new Collection; - if (null !== $first) { - $start = $first->date; - } - if ($date < $start) { - [$start, $date] = [$date, $start]; // @codeCoverageIgnore - } - /** @var array $dates */ - $dates = app('navigation')->blockPeriods($start, $date, $range); - - foreach ($dates as $currentDate) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setAllAssetAccounts()->setRange($currentDate['start'], $currentDate['end'])->withOpposingAccount()->setTypes($types); - $collector->removeFilter(InternalTransferFilter::class); - $journals = $collector->getJournals(); - - if ($journals->count() > 0) { - $sums = $this->sumPerCurrency($journals); - $dateName = app('navigation')->periodShow($currentDate['start'], $currentDate['period']); - $sum = $journals->sum('transaction_amount'); - /** @noinspection PhpUndefinedMethodInspection */ - $entries->push( - [ - 'name' => $dateName, - 'sums' => $sums, - 'sum' => $sum, - 'start' => $currentDate['start']->format('Y-m-d'), - 'end' => $currentDate['end']->format('Y-m-d'), - ] - ); - } - } - - return $entries; - } - - /** - * Collect the sum per currency. - * - * @param Collection $collection - * - * @return array - */ - protected function sumPerCurrency(Collection $collection): array // helper for transactions (math, calculations) - { - $return = []; - /** @var Transaction $transaction */ - foreach ($collection as $transaction) { - $currencyId = (int)$transaction->transaction_currency_id; - - // save currency information: - if (!isset($return[$currencyId])) { - $currencySymbol = $transaction->transaction_currency_symbol; - $decimalPlaces = $transaction->transaction_currency_dp; - $currencyCode = $transaction->transaction_currency_code; - $return[$currencyId] = [ - 'currency' => [ - 'id' => $currencyId, - 'code' => $currencyCode, - 'symbol' => $currencySymbol, - 'dp' => $decimalPlaces, - ], - 'sum' => '0', - 'count' => 0, - ]; - } - // save amount: - $return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], $transaction->transaction_amount); - ++$return[$currencyId]['count']; - } - asort($return); - - return $return; - } } diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php new file mode 100644 index 0000000000..fdd3702259 --- /dev/null +++ b/app/Support/Http/Controllers/AugumentData.php @@ -0,0 +1,143 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Http\Controllers; + +use Carbon\Carbon; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use Illuminate\Support\Collection; + +/** + * Trait AugumentData + * + * @package FireflyIII\Support\Http\Controllers + */ +trait AugumentData +{ + /** + * Get the account names belonging to a bunch of account ID's. + * + * @param array $accountIds + * + * @return array + */ + protected function getAccountNames(array $accountIds): array // extract info from array. + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::CASH]); + $grouped = $accounts->groupBy('id')->toArray(); + $return = []; + foreach ($accountIds as $accountId) { + if (isset($grouped[$accountId])) { + $return[$accountId] = $grouped[$accountId][0]['name']; + } + } + $return[0] = '(no name)'; + + return $return; + } + + /** + * Get the budget names from a set of budget ID's. + * + * @param array $budgetIds + * + * @return array + */ + protected function getBudgetNames(array $budgetIds): array // extract info from array. + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $budgets = $repository->getBudgets(); + $grouped = $budgets->groupBy('id')->toArray(); + $return = []; + foreach ($budgetIds as $budgetId) { + if (isset($grouped[$budgetId])) { + $return[$budgetId] = $grouped[$budgetId][0]['name']; + } + } + $return[0] = (string)trans('firefly.no_budget'); + + return $return; + } + + /** + * Get the category names from a set of category ID's. Small helper function for some of the charts. + * + * @param array $categoryIds + * + * @return array + */ + protected function getCategoryNames(array $categoryIds): array // extract info from array. + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $categories = $repository->getCategories(); + $grouped = $categories->groupBy('id')->toArray(); + $return = []; + foreach ($categoryIds as $categoryId) { + if (isset($grouped[$categoryId])) { + $return[$categoryId] = $grouped[$categoryId][0]['name']; + } + } + $return[0] = (string)trans('firefly.noCategory'); + + return $return; + } + + + /** @noinspection MoreThanThreeArgumentsInspection */ + /** + * Returns the budget limits belonging to the given budget and valid on the given day. + * + * @param Collection $budgetLimits + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + protected function filterBudgetLimits(Collection $budgetLimits, Budget $budget, Carbon $start, Carbon $end): Collection // filter data + { + $set = $budgetLimits->filter( + function (BudgetLimit $budgetLimit) use ($budget, $start, $end) { + if ($budgetLimit->budget_id === $budget->id + && $budgetLimit->start_date->lte($start) // start of budget limit is on or before start + && $budgetLimit->end_date->gte($end) // end of budget limit is on or after end + ) { + return $budgetLimit; + } + + return false; + } + ); + + return $set; + } +} \ No newline at end of file diff --git a/app/Support/Http/Controllers/BasicDataSupport.php b/app/Support/Http/Controllers/BasicDataSupport.php index 5612c58c94..ee6855051e 100644 --- a/app/Support/Http/Controllers/BasicDataSupport.php +++ b/app/Support/Http/Controllers/BasicDataSupport.php @@ -30,6 +30,33 @@ namespace FireflyIII\Support\Http\Controllers; */ trait BasicDataSupport { + + /** + * Filters empty results from getBudgetPeriodReport. + * + * @param array $data + * + * @return array + */ + protected function filterPeriodReport(array $data): array // helper function for period overview. + { + /** + * @var int $entryId + * @var array $set + */ + foreach ($data as $entryId => $set) { + $sum = '0'; + foreach ($set['entries'] as $amount) { + $sum = bcadd($amount, $sum); + } + $data[$entryId]['sum'] = $sum; + if (0 === bccomp('0', $sum)) { + unset($data[$entryId]); + } + } + + return $data; + } /** * Sum up an array. * diff --git a/app/Support/Http/Controllers/PeriodOverview.php b/app/Support/Http/Controllers/PeriodOverview.php new file mode 100644 index 0000000000..f504673782 --- /dev/null +++ b/app/Support/Http/Controllers/PeriodOverview.php @@ -0,0 +1,411 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Http\Controllers; + +use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Helpers\Filter\InternalTransferFilter; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Category; +use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\CacheProperties; +use Illuminate\Support\Collection; + +/** + * Trait PeriodOverview. + * + * General working of an overview thing: + * - Take a start date (session or view, depends on argument). + * - Take end date. This is usually the very first object related to the period overview (first transaction in tag, etc). + * - Smart list of period, becoming larger in time: + * -- This year: months + * -- Last year: quarters + * -- Before that: years + * -- Before that: decennia + * + * - Group expenses, income, etc. under this period. + * - Returns collection of arrays. Possible fields are: + * - start (Carbon), end (Carbon), title (string), spent (string), earned (string), transferred (string) + * + * + * @package FireflyIII\Support\Http\Controllers + */ +trait PeriodOverview +{ + + + /** + * This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range) + * and for each period, the amount of money spent and earned. This is a complex operation which is cached for + * performance reasons. + * + * TODO refactor me. + * + * @param Account $account the account involved + * @param Carbon|null $date + * + * @return Collection + */ + protected function getAccountPeriodOverview(Account $account, ?Carbon $date): Collection // period overview + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $range = app('preferences')->get('viewRange', '1M')->data; + $start = $repository->oldestJournalDate($account) ?? Carbon::now()->startOfMonth(); + $end = $date ?? new Carbon; + if ($end < $start) { + [$start, $end] = [$end, $start]; // @codeCoverageIgnore + } + + // properties for cache + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('account-show-period-entries'); + $cache->addProperty($account->id); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + /** @var array $dates */ + $dates = app('navigation')->blockPeriods($start, $end, $range); + $entries = new Collection; + // loop dates + foreach ($dates as $currentDate) { + + // try a collector for income: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::DEPOSIT]) + ->withOpposingAccount(); + $earned = (string)$collector->getJournals()->sum('transaction_amount'); + + // try a collector for expenses: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts(new Collection([$account]))->setRange($currentDate['start'], $currentDate['end'])->setTypes([TransactionType::WITHDRAWAL]) + ->withOpposingAccount(); + $spent = (string)$collector->getJournals()->sum('transaction_amount'); + + $dateName = app('navigation')->periodShow($currentDate['start'], $currentDate['period']); + /** @noinspection PhpUndefinedMethodInspection */ + $entries->push( + [ + 'name' => $dateName, + 'spent' => $spent, + 'earned' => $earned, + 'start' => $currentDate['start']->format('Y-m-d'), + 'end' => $currentDate['end']->format('Y-m-d'), + ] + ); + } + + $cache->store($entries); + + return $entries; + } + + /** + * Gets period overview used for budgets. + * + * TODO refactor me. + * + * @return Collection + */ + protected function getBudgetPeriodOverview(): Collection + { + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $first = $repository->firstNull(); + $start = null === $first ? new Carbon : $first->date; + $range = app('preferences')->get('viewRange', '1M')->data; + $start = app('navigation')->startOfPeriod($start, $range); + $end = app('navigation')->endOfX(new Carbon, $range, null); + $entries = new Collection; + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('no-budget-period-entries'); + + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + $dates = app('navigation')->blockPeriods($start, $end, $range); + foreach ($dates as $date) { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes( + [TransactionType::WITHDRAWAL] + ); + $set = $collector->getJournals(); + $sum = (string)($set->sum('transaction_amount') ?? '0'); + $journals = $set->count(); + /** @noinspection PhpUndefinedMethodInspection */ + $dateStr = $date['end']->format('Y-m-d'); + $dateName = app('navigation')->periodShow($date['end'], $date['period']); + $entries->push( + ['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end'], + 'start' => $date['start'], + 'end' => $date['end'], + + ] + ); + } + $cache->store($entries); + + return $entries; + } + + /** + * Get a period overview for category. + * + * TODO refactor me. + * + * @param Category $category + * @param Carbon $date + * + * @return Collection + */ + protected function getCategoryPeriodOverview(Category $category, Carbon $date): Collection // periodOverview method + { + /** @var JournalRepositoryInterface $journalRepository */ + $journalRepository = app(JournalRepositoryInterface::class); + /** @var AccountRepositoryInterface $accountRepository */ + $accountRepository = app(AccountRepositoryInterface::class); + /** @var CategoryRepositoryInterface $categoryRepository */ + $categoryRepository = app(CategoryRepositoryInterface::class); + $range = app('preferences')->get('viewRange', '1M')->data; + $first = $journalRepository->firstNull(); + $start = null === $first ? new Carbon : $first->date; + $end = $date ?? new Carbon; + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + + // properties for entries with their amounts. + $cache = new CacheProperties(); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty($range); + $cache->addProperty('categories.entries'); + $cache->addProperty($category->id); + + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + /** @var array $dates */ + $dates = app('navigation')->blockPeriods($start, $end, $range); + $entries = new Collection; + + foreach ($dates as $currentDate) { + $spent = $categoryRepository->spentInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']); + $earned = $categoryRepository->earnedInPeriod(new Collection([$category]), $accounts, $currentDate['start'], $currentDate['end']); + /** @noinspection PhpUndefinedMethodInspection */ + $dateStr = $currentDate['end']->format('Y-m-d'); + $dateName = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); + + // amount transferred + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAllAssetAccounts()->setRange($currentDate['start'], $currentDate['end'])->setCategory($category) + ->withOpposingAccount()->setTypes([TransactionType::TRANSFER]); + $collector->removeFilter(InternalTransferFilter::class); + $transferred = app('steam')->positive((string)$collector->getJournals()->sum('transaction_amount')); + + $entries->push( + [ + 'string' => $dateStr, + 'name' => $dateName, + 'spent' => $spent, + 'earned' => $earned, + 'sum' => bcadd($earned, $spent), + 'transferred' => $transferred, + 'start' => clone $currentDate['start'], + 'end' => clone $currentDate['end'], + ] + ); + } + $cache->store($entries); + + return $entries; + } + + /** + * Get overview of periods for tag. + * + * TODO refactor this. + * + * @param Tag $tag + * + * @return Collection + */ + protected function getTagPeriodOverview(Tag $tag): Collection // period overview for tags. + { + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + // get first and last tag date from tag: + $range = app('preferences')->get('viewRange', '1M')->data; + /** @var Carbon $end */ + $end = app('navigation')->endOfX($repository->lastUseDate($tag) ?? new Carbon, $range, null); + $start = $repository->firstUseDate($tag) ?? new Carbon; + + + // properties for entries with their amounts. + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('tag.entries'); + $cache->addProperty($tag->id); + + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + + $collection = new Collection; + $currentEnd = clone $end; + // while end larger or equal to start + while ($currentEnd >= $start) { + $currentStart = app('navigation')->startOfPeriod($currentEnd, $range); + + // get expenses and what-not in this period and this tag. + $arr = [ + 'string' => $end->format('Y-m-d'), + 'name' => app('navigation')->periodShow($currentEnd, $range), + 'start' => clone $currentStart, + 'end' => clone $currentEnd, + 'date' => clone $end, + 'spent' => $repository->spentInPeriod($tag, $currentStart, $currentEnd), + 'earned' => $repository->earnedInPeriod($tag, $currentStart, $currentEnd), + ]; + $collection->push($arr); + + /** @var Carbon $currentEnd */ + $currentEnd = clone $currentStart; + $currentEnd->subDay(); + } + $cache->store($collection); + + return $collection; + } + + /** + * Get period overview for index. + * + * TODO refactor me. + * + * @param string $what + * @param Carbon $date + * + * @return Collection + */ + protected function getTransactionPeriodOverview(string $what, Carbon $date): Collection // period overview for transactions. + { + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $range = app('preferences')->get('viewRange', '1M')->data; + $first = $repository->firstNull(); + $start = Carbon::now()->subYear(); + $types = config('firefly.transactionTypesByWhat.' . $what); + $entries = new Collection; + if (null !== $first) { + $start = $first->date; + } + if ($date < $start) { + [$start, $date] = [$date, $start]; // @codeCoverageIgnore + } + + /** @var array $dates */ + $dates = app('navigation')->blockPeriods($start, $date, $range); + + foreach ($dates as $currentDate) { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAllAssetAccounts()->setRange($currentDate['start'], $currentDate['end'])->withOpposingAccount()->setTypes($types); + $collector->removeFilter(InternalTransferFilter::class); + $journals = $collector->getJournals(); + + if ($journals->count() > 0) { + $sums = $this->sumPerCurrency($journals); + $dateName = app('navigation')->periodShow($currentDate['start'], $currentDate['period']); + $sum = $journals->sum('transaction_amount'); + /** @noinspection PhpUndefinedMethodInspection */ + $entries->push( + [ + 'name' => $dateName, + 'sums' => $sums, + 'sum' => $sum, + 'start' => $currentDate['start']->format('Y-m-d'), + 'end' => $currentDate['end']->format('Y-m-d'), + ] + ); + } + } + + return $entries; + } + + /** + * Collect the sum per currency. + * + * @param Collection $collection + * + * @return array + */ + protected function sumPerCurrency(Collection $collection): array // helper for transactions (math, calculations) + { + $return = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + $currencyId = (int)$transaction->transaction_currency_id; + + // save currency information: + if (!isset($return[$currencyId])) { + $currencySymbol = $transaction->transaction_currency_symbol; + $decimalPlaces = $transaction->transaction_currency_dp; + $currencyCode = $transaction->transaction_currency_code; + $return[$currencyId] = [ + 'currency' => [ + 'id' => $currencyId, + 'code' => $currencyCode, + 'symbol' => $currencySymbol, + 'dp' => $decimalPlaces, + ], + 'sum' => '0', + 'count' => 0, + ]; + } + // save amount: + $return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], $transaction->transaction_amount); + ++$return[$currencyId]['count']; + } + asort($return); + + return $return; + } + +} \ No newline at end of file