From 8b81526e5444681afb69425a6bf8fdb208c52992 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 30 Oct 2021 11:42:35 +0200 Subject: [PATCH] Various improvs to the report generator. --- app/Helpers/Report/PopupReport.php | 6 +- .../Controllers/Popup/ReportController.php | 1 - .../Controllers/Report/CategoryController.php | 7 +- .../Category/OperationsRepository.php | 135 +++++++++++++++++- .../OperationsRepositoryInterface.php | 30 ++++ .../Report/Budget/BudgetReportGenerator.php | 2 +- .../Category/CategoryReportGenerator.php | 7 +- .../Bulk/ValidatesBulkTransactionQuery.php | 6 +- 8 files changed, 177 insertions(+), 17 deletions(-) diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index 81df47e139..d2b36349fe 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -210,10 +210,9 @@ class PopupReport implements PopupReportInterface // set report accounts + the request accounts: //$set = $attributes['accounts'] ?? new Collection; - $set = new Collection; - $set->push($account); + //$set->push($account); - $collector->setBothAccounts($set) + $collector->setDestinationAccounts(new Collection([$account])) ->setRange($attributes['startDate'], $attributes['endDate']) ->withAccountInformation() ->withBudgetInformation() @@ -223,7 +222,6 @@ class PopupReport implements PopupReportInterface if (null !== $currency) { $collector->setCurrency($currency); } - return $collector->getExtractedJournals(); } diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 1b4d64d685..b5a442c988 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -59,7 +59,6 @@ class ReportController extends Controller 'category-entry' => $this->categoryEntry($attributes), 'budget-entry' => $this->budgetEntry($attributes), }; - return response()->json(['html' => $html]); } } diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index 26bc06f4d1..a9643b37f0 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -648,10 +648,9 @@ class CategoryController extends Controller * @param Carbon $start * @param Carbon $end * - * @return mixed|string - * @throws JsonException + * @return string */ - public function operations(Collection $accounts, Carbon $start, Carbon $end) + public function operations(Collection $accounts, Carbon $start, Carbon $end): string { // chart properties for cache: $cache = new CacheProperties; @@ -673,7 +672,7 @@ class CategoryController extends Controller try { - $result = prefixView('reports.partials.categories', compact('report'))->render(); + $result = (string)prefixView('reports.partials.categories', compact('report'))->render(); $cache->store($result); } catch (Throwable $e) { // @phpstan-ignore-line Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index 9e9af1504d..7c72218355 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -35,8 +35,7 @@ use Illuminate\Support\Collection; */ class OperationsRepository implements OperationsRepositoryInterface { - /** @var User */ - private $user; + private User $user; /** * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period @@ -340,4 +339,136 @@ class OperationsRepository implements OperationsRepositoryInterface { return $this->user->categories()->get(); } + + /** + * @inheritDoc + */ + public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) + ->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts); + if (null !== $categories && $categories->count() > 0) { + $collector->setCategories($categories); + } + if (null === $categories || (null !== $categories && 0 === $categories->count())) { + $collector->setCategories($this->getCategories()); + } + $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $categoryId = (int)$journal['category_id']; + $categoryName = (string)$journal['category_name']; + + // catch "no category" entries. + if (0 === $categoryId) { + continue; + } + + // info about the currency: + $array[$currencyId] = $array[$currencyId] ?? [ + 'categories' => [], + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + + // info about the categories: + $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + 'id' => $categoryId, + 'name' => $categoryName, + 'transaction_journals' => [], + ]; + + // add journal to array: + // only a subset of the fields. + $journalId = (int)$journal['transaction_journal_id']; + $array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [ + 'amount' => app('steam')->positive($journal['amount']), + 'date' => $journal['date'], + 'source_account_id' => $journal['source_account_id'], + 'category_name' => $journal['category_name'], + 'source_account_name' => $journal['source_account_name'], + 'destination_account_id' => $journal['destination_account_id'], + 'destination_account_name' => $journal['destination_account_name'], + 'description' => $journal['description'], + 'transaction_group_id' => $journal['transaction_group_id'], + ]; + + } + + return $array; + } + + /** + * @inheritDoc + */ + public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]) + ->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts); + if (null !== $categories && $categories->count() > 0) { + $collector->setCategories($categories); + } + if (null === $categories || (null !== $categories && 0 === $categories->count())) { + $collector->setCategories($this->getCategories()); + } + $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation(); + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $categoryId = (int)$journal['category_id']; + $categoryName = (string)$journal['category_name']; + + // catch "no category" entries. + if (0 === $categoryId) { + continue; + } + + // info about the currency: + $array[$currencyId] = $array[$currencyId] ?? [ + 'categories' => [], + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + + // info about the categories: + $array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [ + 'id' => $categoryId, + 'name' => $categoryName, + 'transaction_journals' => [], + ]; + + // add journal to array: + // only a subset of the fields. + $journalId = (int)$journal['transaction_journal_id']; + $array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [ + 'amount' => app('steam')->negative($journal['amount']), + 'date' => $journal['date'], + 'source_account_id' => $journal['source_account_id'], + 'category_name' => $journal['category_name'], + 'source_account_name' => $journal['source_account_name'], + 'destination_account_id' => $journal['destination_account_id'], + 'destination_account_name' => $journal['destination_account_name'], + 'description' => $journal['description'], + 'transaction_group_id' => $journal['transaction_group_id'], + ]; + + } + + return $array; + } } diff --git a/app/Repositories/Category/OperationsRepositoryInterface.php b/app/Repositories/Category/OperationsRepositoryInterface.php index 74fc870394..38eab4b695 100644 --- a/app/Repositories/Category/OperationsRepositoryInterface.php +++ b/app/Repositories/Category/OperationsRepositoryInterface.php @@ -47,6 +47,36 @@ interface OperationsRepositoryInterface */ public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; + /** + * This method returns a list of all the transfer transaction journals (as arrays) set in that period + * which have the specified category set to them, transferred INTO the listed accounts. + * It excludes any transfers between the listed accounts. + * It's grouped per currency, with as few details in the array as possible. Amounts are always negative. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * @param Collection|null $categories + * + * @return array + */ + public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array; + + /** + * This method returns a list of all the transfer transaction journals (as arrays) set in that period + * which have the specified category set to them, transferred FROM the listed accounts. + * It excludes any transfers between the listed accounts. + * It's grouped per currency, with as few details in the array as possible. Amounts are always negative. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * @param Collection|null $categories + * + * @return array + */ + public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array; + /** * This method returns a list of all the deposit transaction journals (as arrays) set in that period * which have the specified category set to them. It's grouped per currency, with as few details in the array diff --git a/app/Support/Report/Budget/BudgetReportGenerator.php b/app/Support/Report/Budget/BudgetReportGenerator.php index 860804bfd1..4931d578a7 100644 --- a/app/Support/Report/Budget/BudgetReportGenerator.php +++ b/app/Support/Report/Budget/BudgetReportGenerator.php @@ -249,7 +249,7 @@ class BudgetReportGenerator 'budget_limits' => [], ]; - $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end); + $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); foreach ($noBudget as $noBudgetEntry) { // currency information: diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index cdaf8ac5e1..5d6c25d0a6 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -66,6 +66,11 @@ class CategoryReportGenerator { $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); + + // also transferred out and transferred into these accounts in this category: + $transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts); + $transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts); + $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); @@ -75,7 +80,7 @@ class CategoryReportGenerator ]; // needs four for-each loops. - foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout] as $data) { + foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout, $transferredIn, $transferredOut] as $data) { $this->processOpsArray($data); } } diff --git a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php index 66175ddb8f..a4e5146fc4 100644 --- a/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php +++ b/app/Validation/Api/Data/Bulk/ValidatesBulkTransactionQuery.php @@ -41,15 +41,13 @@ trait ValidatesBulkTransactionQuery protected function validateTransactionQuery(Validator $validator): void { $data = $validator->getData(); - // assumption is all validation has already taken place - // and the query key exists. + // assumption is all validation has already taken place and the query key exists. $json = json_decode($data['query'], true, 8, JSON_THROW_ON_ERROR); if (array_key_exists('account_id', $json['where']) && array_key_exists('account_id', $json['update']) ) { - // find both accounts - // must be same type. + // find both accounts, must be same type. // already validated: belongs to this user. $repository = app(AccountRepositoryInterface::class); $source = $repository->find((int)$json['where']['account_id']);