Refactor various methods away from controllers

This commit is contained in:
James Cole 2018-08-11 06:39:29 +02:00
parent ec2463a3ba
commit 16fba15b5c
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
13 changed files with 609 additions and 527 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 */
/**

View File

@ -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 */
/**

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,143 @@
<?php
/**
* AugumentData.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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.
*

View File

@ -0,0 +1,411 @@
<?php
/**
* PeriodOverview.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}