diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index fa00d2e0ee..faf5a15d1d 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -74,7 +74,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $reportType = 'category'; $expenses = $this->getExpenses(); $income = $this->getIncome(); - $accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income)); + $accountSummary = $this->getObjectSummary($this->summarizeByAssetAccount($expenses), $this->summarizeByAssetAccount($income)); $categorySummary = $this->getObjectSummary($this->summarizeByCategory($expenses), $this->summarizeByCategory($income)); $averageExpenses = $this->getAverages($expenses, SORT_ASC); $averageIncome = $this->getAverages($income, SORT_DESC); diff --git a/app/Generator/Report/Support.php b/app/Generator/Report/Support.php index 4ea3d00d1e..1f5e8d3b78 100644 --- a/app/Generator/Report/Support.php +++ b/app/Generator/Report/Support.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Generator\Report; -use Illuminate\Support\Collection; +use FireflyIII\Models\TransactionType; /** * Class Support. @@ -146,6 +146,7 @@ class Support * @var string $entry */ foreach ($earned as $objectId => $entry) { + $entry = bcmul($entry, '-1'); if (!isset($return[$objectId])) { $return[$objectId] = ['spent' => '0', 'earned' => '0']; } @@ -176,4 +177,36 @@ class Support return $result; } + + /** + * Summarize the data by the asset account or liability, depending on the type. + * + * In case of transfers, it will choose the source account. + * + * @param array $array + * + * @return array + */ + protected function summarizeByAssetAccount(array $array): array + { + $result = []; + /** @var array $journal */ + foreach ($array as $journal) { + $accountId = 0; + switch ($journal['transaction_type_type']) { + case TransactionType::WITHDRAWAL: + case TransactionType::TRANSFER: + $accountId = $journal['source_account_id'] ?? 0; + break; + case TransactionType::DEPOSIT: + $accountId = $journal['destination_account_id'] ?? 0; + break; + } + + $result[$accountId] = $result[$accountId] ?? '0'; + $result[$accountId] = bcadd($journal['amount'], $result[$accountId]); + } + + return $result; + } } diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index 63eb17c81c..749368bd2e 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -24,11 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Chart; use Carbon\Carbon; -use FireflyIII\Helpers\Collector\TransactionCollectorInterface; -use FireflyIII\Helpers\Filter\NegativeAmountFilter; -use FireflyIII\Helpers\Filter\OpposingAccountFilter; -use FireflyIII\Helpers\Filter\PositiveAmountFilter; -use FireflyIII\Helpers\Filter\TransferFilter; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; @@ -48,9 +44,9 @@ class MetaPieChart implements MetaPieChartInterface /** @var array The ways to group transactions, given the type of chart. */ static protected $grouping = [ - 'account' => ['opposing_account_id'], - 'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'], - 'category' => ['transaction_journal_category_id', 'transaction_category_id'], + 'account' => ['destination_account_id'], + 'budget' => ['budget_id'], + 'category' => ['category_id'], 'tag' => [], ]; /** @var Collection Involved accounts. */ @@ -114,25 +110,29 @@ class MetaPieChart implements MetaPieChartInterface // also collect all other transactions if ($this->collectOtherObjects && 'expense' === $direction) { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getTransactions(); - $sum = (string)$journals->sum('transaction_amount'); + $journals = $collector->getExtractedJournals(); + $sum = $this->sumJournals($journals); $sum = bcmul($sum, '-1'); $sum = bcsub($sum, $this->total); $chartData[$key] = $sum; } if ($this->collectOtherObjects && 'income' === $direction) { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getTransactions(); - $sum = (string)$journals->sum('transaction_amount'); + $journals = $collector->getExtractedJournals(); + $sum = $this->sumJournals($journals); $sum = bcsub($sum, $this->total); $chartData[$key] = $sum; } @@ -140,6 +140,152 @@ class MetaPieChart implements MetaPieChartInterface return $chartData; } + /** + * Get all transactions. + * + * @param string $direction + * + * @return array + */ + protected function getTransactions(string $direction): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; + if ('expense' === $direction) { + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + } + + $collector->setUser($this->user); + $collector->setAccounts($this->accounts); + $collector->setRange($this->start, $this->end); + $collector->setTypes($types); + $collector->withAccountInformation(); + + $collector->setBudgets($this->budgets); + $collector->setCategories($this->categories); + + $collector->withCategoryInformation(); + $collector->withBudgetInformation(); + + // @codeCoverageIgnoreStart + if ($this->tags->count() > 0) { + $collector->setTags($this->tags); + } + + // @codeCoverageIgnoreEnd + + return $collector->getExtractedJournals(); + } + + /** + * Group by a specific field. + * + * @param array $array + * @param array $fields + * + * @return array + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * + */ + protected function groupByFields(array $array, array $fields): array + { + $grouped = []; + if (0 === count($fields) && $this->tags->count() > 0) { + // do a special group on tags: + $grouped = $this->groupByTag($array); // @codeCoverageIgnore + } + + if (0 !== count($fields) || $this->tags->count() <= 0) { + $grouped = []; + /** @var array $journal */ + foreach ($array as $journal) { + $values = []; + foreach ($fields as $field) { + $values[] = (int)$journal[$field]; + } + $value = max($values); + $grouped[$value] = $grouped[$value] ?? '0'; + $grouped[$value] = bcadd($journal['amount'], $grouped[$value]); + } + } + + return $grouped; + } + + /** + * Group by tag (slightly different). + * + * @codeCoverageIgnore + * + * @param Collection $set + * + * @return array + */ + private function groupByTag(Collection $set): array + { + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $journal = $transaction->transactionJournal; + $tags = $journal->tags; + /** @var Tag $tag */ + foreach ($tags as $tag) { + $tagId = $tag->id; + $grouped[$tagId] = $grouped[$tagId] ?? '0'; + $grouped[$tagId] = bcadd($transaction->transaction_amount, $grouped[$tagId]); + } + } + + return $grouped; + } + + /** + * Organise by certain type. + * + * @param string $type + * @param array $array + * + * @return array + */ + protected function organizeByType(string $type, array $array): array + { + $chartData = []; + $names = []; + $repository = app($this->repositories[$type]); + $repository->setUser($this->user); + foreach ($array as $objectId => $amount) { + if (!isset($names[$objectId])) { + $object = $repository->findNull((int)$objectId); + $name = null === $object ? '(no name)' : $object->name; + $names[$objectId] = $name ?? $object->tag; + } + $amount = app('steam')->positive($amount); + $this->total = bcadd($this->total, $amount); + $chartData[$names[$objectId]] = $amount; + } + + return $chartData; + } + + /** + * @param array $journals + * @return string + */ + private function sumJournals(array $journals): string + { + $sum = '0'; + /** @var array $journal */ + foreach ($journals as $journal) { + $amount = (string)$journal['amount']; + $sum = bcadd($sum, $amount); + } + + return $sum; + } + /** * Accounts setter. * @@ -267,140 +413,4 @@ class MetaPieChart implements MetaPieChartInterface return $this; } - - /** - * Get all transactions. - * - * @param string $direction - * - * @return Collection - */ - protected function getTransactions(string $direction): Collection - { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $collector->addFilter(NegativeAmountFilter::class); - if ('expense' === $direction) { - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $collector->addFilter(PositiveAmountFilter::class); - $collector->removeFilter(NegativeAmountFilter::class); - } - - $collector->setUser($this->user); - $collector->setAccounts($this->accounts); - $collector->setRange($this->start, $this->end); - $collector->setTypes($types); - $collector->withOpposingAccount(); - $collector->addFilter(OpposingAccountFilter::class); - - if ('income' === $direction) { - $collector->removeFilter(TransferFilter::class); - } - - $collector->setBudgets($this->budgets); - $collector->setCategories($this->categories); - - // @codeCoverageIgnoreStart - if ($this->tags->count() > 0) { - $collector->setTags($this->tags); - $collector->withCategoryInformation(); - $collector->withBudgetInformation(); - } - - // @codeCoverageIgnoreEnd - - return $collector->getTransactions(); - } - - /** - * Group by a specific field. - * - * @param Collection $set - * @param array $fields - * - * @return array - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * - */ - protected function groupByFields(Collection $set, array $fields): array - { - $grouped = []; - if (0 === \count($fields) && $this->tags->count() > 0) { - // do a special group on tags: - $grouped = $this->groupByTag($set); // @codeCoverageIgnore - } - - if (0 !== \count($fields) || $this->tags->count() <= 0) { - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $values = []; - foreach ($fields as $field) { - $values[] = (int)$transaction->$field; - } - $value = max($values); - $grouped[$value] = $grouped[$value] ?? '0'; - $grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]); - } - } - - return $grouped; - } - - /** - * Organise by certain type. - * - * @param string $type - * @param array $array - * - * @return array - */ - protected function organizeByType(string $type, array $array): array - { - $chartData = []; - $names = []; - $repository = app($this->repositories[$type]); - $repository->setUser($this->user); - foreach ($array as $objectId => $amount) { - if (!isset($names[$objectId])) { - $object = $repository->findNull((int)$objectId); - $name = null === $object ? '(no name)' : $object->name; - $names[$objectId] = $name ?? $object->tag; - } - $amount = app('steam')->positive($amount); - $this->total = bcadd($this->total, $amount); - $chartData[$names[$objectId]] = $amount; - } - - return $chartData; - } - - /** - * Group by tag (slightly different). - * - * @codeCoverageIgnore - * - * @param Collection $set - * - * @return array - */ - private function groupByTag(Collection $set): array - { - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $journal = $transaction->transactionJournal; - $tags = $journal->tags; - /** @var Tag $tag */ - foreach ($tags as $tag) { - $tagId = $tag->id; - $grouped[$tagId] = $grouped[$tagId] ?? '0'; - $grouped[$tagId] = bcadd($transaction->transaction_amount, $grouped[$tagId]); - } - } - - return $grouped; - } } diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 7525c72a5b..d774508cd8 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Helpers\Collector; use Carbon\Carbon; +use Carbon\Exceptions\InvalidDateException; use Exception; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; @@ -37,8 +38,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -use function count; -use function get_class; +use Log; /** * Class GroupCollector @@ -226,16 +226,28 @@ class GroupCollector implements GroupCollectorInterface { $result = $augmentedGroup->toArray(); $result['tags'] = []; - $result['tag_ids'] = []; $result['date'] = new Carbon($result['date']); $result['created_at'] = new Carbon($result['created_at']); $result['updated_at'] = new Carbon($result['updated_at']); $result['reconciled'] = 1 === (int)$result['reconciled']; - if (isset($augmentedGroup['tag'])) { - $result['tags'][] = $augmentedGroup['tag']; - } - if (isset($augmentedGroup['tag_id'])) { - $result['tag_ids'][] = $augmentedGroup['tag_id']; + if (isset($augmentedGroup['tag_id'])) { // assume the other fields are present as well. + $tagId = (int)$augmentedGroup['tag_id']; + $tagDate = null; + try { + $tagDate = Carbon::parse($augmentedGroup['tag_date']); + } catch (InvalidDateException $e) { + Log::debug(sprintf('Could not parse date: %s', $e->getMessage())); + } + + $result['tags'][$tagId] = [ + 'id' => (int)$result['tag_id'], + 'name' => $result['tag_name'], + 'date' => $tagDate, + 'description' => $result['tag_description'], + 'latitude' => $result['tag_latitude'], + 'longitude' => $result['tag_longitude'], + 'zoom_level' => $result['tag_zoom_level'], + ]; } return $result; @@ -249,15 +261,26 @@ class GroupCollector implements GroupCollectorInterface private function mergeTags(array $existingJournal, TransactionGroup $newGroup): array { $newArray = $newGroup->toArray(); - if (isset($newArray['tag_id'])) { - $existingJournal['tag_ids'][] = (int)$newArray['tag_id']; - } - if (isset($newArray['tag'])) { - $existingJournal['tags'][] = $newArray['tag']; + if (isset($newArray['tag_id'])) { // assume the other fields are present as well. + $tagId = (int)$newGroup['tag_id']; + $tagDate = null; + try { + $tagDate = Carbon::parse($newArray['tag_date']); + } catch (InvalidDateException $e) { + Log::debug(sprintf('Could not parse date: %s', $e->getMessage())); + } + + $existingJournal['tags'][$tagId] = [ + 'id' => (int)$newArray['tag_id'], + 'name' => $newArray['tag_name'], + 'date' => $tagDate, + 'description' => $newArray['tag_description'], + 'latitude' => $newArray['tag_latitude'], + 'longitude' => $newArray['tag_longitude'], + 'zoom_level' => $newArray['tag_zoom_level'], + ]; } - $existingJournal['tags'] = array_unique($existingJournal['tags']); - $existingJournal['tag_ids'] = array_unique($existingJournal['tag_ids']); return $existingJournal; } @@ -403,8 +426,10 @@ class GroupCollector implements GroupCollectorInterface */ public function setBudgets(Collection $budgets): GroupCollectorInterface { - $this->withBudgetInformation(); - $this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray()); + if ($budgets->count() > 0) { + $this->withBudgetInformation(); + $this->query->whereIn('budgets.id', $budgets->pluck('id')->toArray()); + } return $this; } @@ -560,7 +585,12 @@ class GroupCollector implements GroupCollectorInterface $this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); $this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id'); $this->fields[] = 'tags.id as tag_id'; - $this->fields[] = 'tags.tag as tag'; + $this->fields[] = 'tags.tag as tag_name'; + $this->fields[] = 'tags.date as tag_date'; + $this->fields[] = 'tags.description as tag_description'; + $this->fields[] = 'tags.latitude as tag_latitude'; + $this->fields[] = 'tags.longitude as tag_longitude'; + $this->fields[] = 'tags.zoomLevel as tag_zoom_level'; } } @@ -726,8 +756,10 @@ class GroupCollector implements GroupCollectorInterface */ public function setCategories(Collection $categories): GroupCollectorInterface { - $this->withCategoryInformation(); - $this->query->where('categories.id', $categories->pluck('id')->toArray()); + if ($categories->count() > 0) { + $this->withCategoryInformation(); + $this->query->whereIn('categories.id', $categories->pluck('id')->toArray()); + } return $this; } diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index d8f5a183c6..00cf0d3f19 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -24,7 +24,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; -use FireflyIII\Helpers\Collector\TransactionCollectorInterface; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Models\Attachment; use FireflyIII\Models\Bill; @@ -124,7 +124,7 @@ class BillController extends Controller * Destroy a bill. * * @param Request $request - * @param Bill $bill + * @param Bill $bill * * @return RedirectResponse|\Illuminate\Routing\Redirector */ @@ -143,7 +143,7 @@ class BillController extends Controller * Edit a bill. * * @param Request $request - * @param Bill $bill + * @param Bill $bill * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ @@ -232,7 +232,7 @@ class BillController extends Controller * Rescan bills for transactions. * * @param Request $request - * @param Bill $bill + * @param Bill $bill * * @return RedirectResponse|\Illuminate\Routing\Redirector * @throws \FireflyIII\Exceptions\FireflyException @@ -269,7 +269,7 @@ class BillController extends Controller * Show a bill. * * @param Request $request - * @param Bill $bill + * @param Bill $bill * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ @@ -304,13 +304,12 @@ class BillController extends Controller $object = $manager->createData($resource)->toArray(); $object['data']['currency'] = $bill->transactionCurrency; - // use collector: - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation() - ->withCategoryInformation(); - $transactions = $collector->getPaginatedTransactions(); - $transactions->setPath(route('bills.show', [$bill->id])); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation() + ->withCategoryInformation()->withAccountInformation(); + $groups = $collector->getPaginatedGroups(); + $groups->setPath(route('bills.show', [$bill->id])); // transform any attachments as well. $collection = $this->billRepository->getAttachments($bill); @@ -326,7 +325,7 @@ class BillController extends Controller } - return view('bills.show', compact('attachments', 'transactions', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle')); + return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle')); } @@ -368,7 +367,7 @@ class BillController extends Controller * Update a bill. * * @param BillFormRequest $request - * @param Bill $bill + * @param Bill $bill * * @return RedirectResponse */ diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index 3879ff540c..845f02d546 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -152,7 +152,7 @@ class BudgetReportController extends Controller $cache->addProperty($start); $cache->addProperty($end); if ($cache->has()) { - return response()->json($cache->get()); // @codeCoverageIgnore + //return response()->json($cache->get()); // @codeCoverageIgnore } $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); $function = app('navigation')->preferredEndOfPeriod($start, $end); diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 4ff0e3b465..ad880ae48b 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -207,7 +207,7 @@ class CategoryReportController extends Controller $cache->addProperty($start); $cache->addProperty($end); if ($cache->has()) { - return response()->json($cache->get()); // @codeCoverageIgnore + //return response()->json($cache->get()); // @codeCoverageIgnore } $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); @@ -261,7 +261,7 @@ class CategoryReportController extends Controller $labelOut = $category->id . '-out'; $labelSumIn = $category->id . '-total-in'; $labelSumOut = $category->id . '-total-out'; - $currentIncome = $income[$category->id] ?? '0'; + $currentIncome = bcmul($income[$category->id] ?? '0','-1'); $currentExpense = $expenses[$category->id] ?? '0'; // add to sum: @@ -279,6 +279,7 @@ class CategoryReportController extends Controller /** @var Carbon $currentStart */ $currentStart = clone $currentEnd; $currentStart->addDay(); + $currentStart->startOfDay(); } // remove all empty entries to prevent cluttering: $newSet = []; @@ -287,7 +288,7 @@ class CategoryReportController extends Controller $newSet[$key] = $chartData[$key]; } } - if (0 === \count($newSet)) { + if (0 === count($newSet)) { $newSet = $chartData; } $data = $this->generator->multiSet($newSet); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 4f9d13c62a..126798978a 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use Exception; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Http\Middleware\Installer; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -35,7 +35,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Log; -use Exception; + /** * Class HomeController. */ @@ -56,8 +56,8 @@ class HomeController extends Controller * Change index date range. * * @param Request $request - * @throws Exception * @return JsonResponse + * @throws Exception */ public function dateRange(Request $request): JsonResponse { @@ -97,8 +97,8 @@ class HomeController extends Controller * Show index. * * @param AccountRepositoryInterface $repository - * @throws Exception * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + * @throws Exception */ public function index(AccountRepositoryInterface $repository) { diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index 29ca5dd501..788c6c2e17 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -44,8 +44,6 @@ use Illuminate\Support\Collection; */ trait AugumentData { - - /** * Searches for the opposing account. * @@ -78,8 +76,8 @@ trait AugumentData * * @param Collection $assets * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array * @@ -141,8 +139,8 @@ trait AugumentData * * @param Collection $assets * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array */ @@ -219,9 +217,9 @@ trait AugumentData * 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 + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end * * @return Collection */ @@ -352,9 +350,9 @@ trait AugumentData * Get the expenses for a budget in a date range. * * @param Collection $limits - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end * * @return array * @@ -388,6 +386,63 @@ trait AugumentData return $return; } + /** + * + * Returns an array with the following values: + * 0 => + * 'name' => name of budget + repetition + * 'left' => left in budget repetition (always zero) + * 'overspent' => spent more than budget repetition? (always zero) + * 'spent' => actually spent in period for budget + * 1 => (etc) + * + * @param Budget $budget + * @param Collection $limits + * + * @return array + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * + */ + protected function spentInPeriodMulti(Budget $budget, Collection $limits): array // get data + augment with info + { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + + $return = []; + $format = (string)trans('config.month_and_day'); + $name = $budget->name; + /** @var BudgetLimit $budgetLimit */ + foreach ($limits as $budgetLimit) { + $expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = app('steam')->positive($expenses); + + if ($limits->count() > 1) { + $name = $budget->name . ' ' . trans( + 'firefly.between_dates', + [ + 'start' => $budgetLimit->start_date->formatLocalized($format), + 'end' => $budgetLimit->end_date->formatLocalized($format), + ] + ); + } + $amount = $budgetLimit->amount; + $leftInLimit = bcsub($amount, $expenses); + $hasOverspent = bccomp($leftInLimit, '0') === -1; + $left = $hasOverspent ? '0' : bcsub($amount, $expenses); + $spent = $hasOverspent ? $amount : $expenses; + $overspent = $hasOverspent ? app('steam')->positive($leftInLimit) : '0'; + + $return[$name] = [ + 'left' => $left, + 'overspent' => $overspent, + 'spent' => $spent, + ]; + } + + return $return; + } + /** * Gets all budget limits for a budget. * @@ -425,25 +480,22 @@ trait AugumentData return $set; } - /** * Helper function that groups expenses. * - * @param Collection $set + * @param array $array * * @return array */ - protected function groupByBudget(Collection $set): array // filter + group data + protected function groupByBudget(array $array): array // filter + group data { // group by category ID: $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $jrnlBudId = (int)$transaction->transaction_journal_budget_id; - $transBudId = (int)$transaction->transaction_budget_id; - $budgetId = max($jrnlBudId, $transBudId); + /** @var array $journal */ + foreach ($array as $journal) { + $budgetId = (int)$journal['budget_id']; $grouped[$budgetId] = $grouped[$budgetId] ?? '0'; - $grouped[$budgetId] = bcadd($transaction->transaction_amount, $grouped[$budgetId]); + $grouped[$budgetId] = bcadd($journal['amount'], $grouped[$budgetId]); } return $grouped; @@ -452,21 +504,19 @@ trait AugumentData /** * Group transactions by category. * - * @param Collection $set + * @param array $array * * @return array */ - protected function groupByCategory(Collection $set): array // filter + group data + protected function groupByCategory(array $array): array // filter + group data { // group by category ID: $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $jrnlCatId = (int)$transaction->transaction_journal_category_id; - $transCatId = (int)$transaction->transaction_category_id; - $categoryId = max($jrnlCatId, $transCatId); + /** @var array $journal */ + foreach($array as $journal) { + $categoryId = (int)$journal['category_id']; $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; - $grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]); + $grouped[$categoryId] = bcadd($journal['amount'], $grouped[$categoryId]); } return $grouped; @@ -493,6 +543,10 @@ trait AugumentData return $grouped; } + + + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * Group transactions by tag. * @@ -519,8 +573,6 @@ trait AugumentData return $grouped; } - - /** @noinspection MoreThanThreeArgumentsInspection */ /** @@ -528,8 +580,8 @@ trait AugumentData * * @param Collection $assets * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -592,8 +644,8 @@ trait AugumentData * * @param Collection $assets * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array * @@ -657,8 +709,8 @@ trait AugumentData * * @param Collection $assets * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array */ @@ -698,65 +750,6 @@ trait AugumentData /** @noinspection MoreThanThreeArgumentsInspection */ - /** - * - * Returns an array with the following values: - * 0 => - * 'name' => name of budget + repetition - * 'left' => left in budget repetition (always zero) - * 'overspent' => spent more than budget repetition? (always zero) - * 'spent' => actually spent in period for budget - * 1 => (etc) - * - * @param Budget $budget - * @param Collection $limits - * - * @return array - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * - */ - protected function spentInPeriodMulti(Budget $budget, Collection $limits): array // get data + augment with info - { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - - $return = []; - $format = (string)trans('config.month_and_day'); - $name = $budget->name; - /** @var BudgetLimit $budgetLimit */ - foreach ($limits as $budgetLimit) { - $expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); - $expenses = app('steam')->positive($expenses); - - if ($limits->count() > 1) { - $name = $budget->name . ' ' . trans( - 'firefly.between_dates', - [ - 'start' => $budgetLimit->start_date->formatLocalized($format), - 'end' => $budgetLimit->end_date->formatLocalized($format), - ] - ); - } - $amount = $budgetLimit->amount; - $leftInLimit = bcsub($amount, $expenses); - $hasOverspent = bccomp($leftInLimit, '0') === -1; - $left = $hasOverspent ? '0' : bcsub($amount, $expenses); - $spent = $hasOverspent ? $amount : $expenses; - $overspent = $hasOverspent ? app('steam')->positive($leftInLimit) : '0'; - - $return[$name] = [ - 'left' => $left, - 'overspent' => $overspent, - 'spent' => $spent, - ]; - } - - return $return; - } - - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * Returns an array with the following values: * 'name' => "no budget" in local language diff --git a/app/Support/Http/Controllers/TransactionCalculation.php b/app/Support/Http/Controllers/TransactionCalculation.php index 16280749b3..f2db26bfdf 100644 --- a/app/Support/Http/Controllers/TransactionCalculation.php +++ b/app/Support/Http/Controllers/TransactionCalculation.php @@ -24,11 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Http\Controllers; use Carbon\Carbon; -use FireflyIII\Helpers\Collector\TransactionCollectorInterface; -use FireflyIII\Helpers\Filter\NegativeAmountFilter; -use FireflyIII\Helpers\Filter\OpposingAccountFilter; -use FireflyIII\Helpers\Filter\PositiveAmountFilter; -use FireflyIII\Helpers\Filter\TransferFilter; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; @@ -43,18 +39,21 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array */ - protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection // get data + augument + protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setOpposingAccounts($opposing); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setAccounts($accounts) + ->setRange($start, $end) + ->setTypes([TransactionType::WITHDRAWAL]) + ->setAccounts($opposing); - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -62,24 +61,21 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $tags - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array * */ - protected function getExpensesForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection // get data + augument + protected function getExpensesForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setTags($tags)->withOpposingAccount(); - $collector->removeFilter(TransferFilter::class); + ->setTags($tags)->withAccountInformation(); - $collector->addFilter(OpposingAccountFilter::class); - $collector->addFilter(PositiveAmountFilter::class); - - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -87,23 +83,19 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $budgets - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array */ - protected function getExpensesInBudgets(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection // get data + augment with info + protected function getExpensesInBudgets(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setBudgets($budgets)->withOpposingAccount(); - $collector->removeFilter(TransferFilter::class); + ->setBudgets($budgets)->withAccountInformation(); - $collector->addFilter(OpposingAccountFilter::class); - $collector->addFilter(PositiveAmountFilter::class); - - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -111,25 +103,25 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $categories - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array * * */ - protected function getExpensesInCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection // get data + augument + protected function getExpensesInCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setCategories($categories)->withOpposingAccount(); - $collector->removeFilter(TransferFilter::class); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setAccounts($accounts) + ->setRange($start, $end) + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($categories) + ->withAccountInformation(); - $collector->addFilter(OpposingAccountFilter::class); - $collector->addFilter(PositiveAmountFilter::class); - - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -137,22 +129,19 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $categories - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array */ - protected function getIncomeForCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection // get data + augument + protected function getIncomeForCategories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($categories)->withOpposingAccount(); + ->setCategories($categories)->withAccountInformation(); - $collector->addFilter(OpposingAccountFilter::class); - $collector->addFilter(NegativeAmountFilter::class); - - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -160,18 +149,19 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $opposing - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * - * @return Collection + * @return array */ - protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection // get data + augument + protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setOpposingAccounts($opposing); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]) + ->setAccounts($opposing); - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } /** @@ -179,24 +169,21 @@ trait TransactionCalculation * * @param Collection $accounts * @param Collection $tags - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return Collection * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ - protected function getIncomeForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection // get data + augument + protected function getIncomeForTags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): array { - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setTags($tags)->withOpposingAccount(); + ->setTags($tags)->withAccountInformation(); - $collector->addFilter(OpposingAccountFilter::class); - $collector->addFilter(NegativeAmountFilter::class); - - return $collector->getTransactions(); + return $collector->getExtractedJournals(); } } diff --git a/resources/views/v1/bills/show.twig b/resources/views/v1/bills/show.twig index 4665d9c7fd..ca87958ab5 100644 --- a/resources/views/v1/bills/show.twig +++ b/resources/views/v1/bills/show.twig @@ -163,7 +163,7 @@