From 9c5d192d90255f9b0935ac35b471dd1c5dedb1f7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 08:27:25 +0100 Subject: [PATCH] Journal collector may not have been a bad idea after all! --- app/Helpers/Collector/JournalCollector.php | 360 ++++++++++++++++++ app/Http/Controllers/JsonController.php | 14 +- .../Controllers/TransactionController.php | 37 +- resources/views/list/journals.twig | 8 + resources/views/transactions/index.twig | 2 +- 5 files changed, 405 insertions(+), 16 deletions(-) create mode 100644 app/Helpers/Collector/JournalCollector.php diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php new file mode 100644 index 0000000000..467197d8ca --- /dev/null +++ b/app/Helpers/Collector/JournalCollector.php @@ -0,0 +1,360 @@ +user = $user; + $this->query = $this->startQuery(); + } + + /** + * @return int + * @throws FireflyException + */ + public function count(): int + { + if ($this->run === true) { + throw new FireflyException('Cannot count after run in JournalCollector.'); + } + + $countQuery = clone $this->query; + + // dont need some fields: + $countQuery->getQuery()->limit = null; + $countQuery->getQuery()->offset = null; + $countQuery->getQuery()->unionLimit = null; + $countQuery->getQuery()->groups = null; + $countQuery->groupBy('accounts.user_id'); + $this->count = $countQuery->count(); + + return $this->count; + } + + /** + * @return Collection + */ + public function getJournals(): Collection + { + $this->run = true; + $set = $this->query->get($this->fields); + + // loop for decryption. + $set->each( + function (Transaction $transaction) { + $transaction->date = new Carbon($transaction->date); + $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; + $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; + } + ); + + return $set; + } + + /** + * It might be worth it to expand this query to include all account information required. + * + * @param Collection $accounts + * @param array $types + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection + { + $accountIds = $accounts->pluck('id')->toArray(); + $query = Transaction + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereIn('transactions.account_id', $accountIds) + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + if (count($types) > 0) { + $query->whereIn('transaction_types.type', $types); + } + + $set = $query->get( + [ + 'transaction_journals.id as journal_id', + 'transaction_journals.description', + 'transaction_journals.date', + 'transaction_journals.encrypted', + //'transaction_journals.transaction_currency_id', + 'transaction_currencies.code as transaction_currency_code', + //'transaction_currencies.symbol as transaction_currency_symbol', + 'transaction_types.type as transaction_type_type', + 'transaction_journals.bill_id', + 'bills.name as bill_name', + 'transactions.id as id', + 'transactions.amount as transaction_amount', + 'transactions.description as transaction_description', + 'transactions.account_id', + 'transactions.identifier', + 'transactions.transaction_journal_id', + 'accounts.name as account_name', + 'accounts.encrypted as account_encrypted', + 'account_types.type as account_type', + + ] + ); + + // loop for decryption. + $set->each( + function (Transaction $transaction) { + $transaction->date = new Carbon($transaction->date); + $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; + $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; + } + ); + + return $set; + } + + /** + * @return LengthAwarePaginator + * @throws FireflyException + */ + public function getPaginatedJournals():LengthAwarePaginator + { + if ($this->run === true) { + throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.'); + } + $this->count(); + $set = $this->getJournals(); + $journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page); + + return $journals; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + } + + return $this; + } + + /** + * @param int $limit + * + * @return JournalCollector + */ + public function setLimit(int $limit): JournalCollector + { + $this->limit = $limit; + $this->query->limit($limit); + Log::debug(sprintf('Set limit to %d', $limit)); + + return $this; + } + + /** + * @param int $offset + * + * @return JournalCollector + */ + public function setOffset(int $offset): JournalCollector + { + $this->offset = $offset; + } + + /** + * @param int $page + * + * @return JournalCollector + */ + public function setPage(int $page): JournalCollector + { + $this->page = $page; + + if ($page > 0) { + $page--; + } + Log::debug(sprintf('Page is %d', $page)); + + if (!is_null($this->limit)) { + $offset = ($this->limit * $page); + $this->offset = $offset; + $this->query->skip($offset); + Log::debug(sprintf('Changed offset to %d', $offset)); + } + if (is_null($this->limit)) { + Log::debug('The limit is zero, cannot set the page.'); + } + + return $this; + } + + /** + * @param Carbon $start + * @param Carbon $end + * + * @return JournalCollector + */ + public function setRange(Carbon $start, Carbon $end): JournalCollector + { + if ($start <= $end) { + $this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d')); + $this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d')); + } + + return $this; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setDestinationAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + $this->query->where('transactions.amount', '>', 0); + } + + return $this; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setSourceAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + $this->query->where('transactions.amount', '<', 0); + } + + return $this; + } + + /** + * @param array $types + * + * @return JournalCollector + */ + public function setTypes(array $types): JournalCollector + { + if (count($types) > 0) { + $this->query->whereIn('transaction_types.type', $types); + } + + return $this; + } + + /** + * @return EloquentBuilder + */ + private function startQuery(): EloquentBuilder + { + + $query = Transaction + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + return $query; + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 5eea913966..197fd7a326 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers; use Amount; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; @@ -270,17 +271,20 @@ class JsonController extends Controller } /** - * @param JournalTaskerInterface $tasker - * @param $what + * @param $what * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function transactionJournals(JournalTaskerInterface $tasker, $what) + public function transactionJournals($what) { $descriptions = []; $type = config('firefly.transactionTypesByWhat.' . $what); $types = [$type]; - $journals = $tasker->getJournals($types, 1, 50); + + // use journal collector instead: + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit(100)->setPage(1); + $journals = $collector->getJournals(); foreach ($journals as $j) { $descriptions[] = $j->description; } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index bd2a3d4c1e..a92c70d130 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -14,7 +14,10 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Http\Request; @@ -49,21 +52,35 @@ class TransactionController extends Controller } /** - * @param Request $request - * @param JournalTaskerInterface $tasker - * @param string $what + * @param Request $request + * @param AccountRepositoryInterface $repository + * @param string $what * * @return View */ - public function index(Request $request, JournalTaskerInterface $tasker, string $what) + public function index(Request $request, AccountRepositoryInterface $repository, string $what) { - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); - $types = config('firefly.transactionTypesByWhat.' . $what); - $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); - $journals = $tasker->getJournals($types, $page, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); + $types = config('firefly.transactionTypesByWhat.' . $what); + $subTitle = trans('firefly.title_' . $what); + $page = intval($request->get('page')); + $assetAccounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page); + // depending on the view, we filter the collector to grab the right stuff. + switch ($what) { + default: + $collector->setAccounts($assetAccounts); + break; + case 'transfer': + case 'transfers': + $collector->setDestinationAccounts($assetAccounts); + break; + } + + $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals')); diff --git a/resources/views/list/journals.twig b/resources/views/list/journals.twig index ddadd0aa60..86fa131105 100644 --- a/resources/views/list/journals.twig +++ b/resources/views/list/journals.twig @@ -1,5 +1,12 @@ {{ journals.render|raw }} +{% if journals.count == 0 %} +

+ {{ 'nothing_to_display'|_ }} +

+{% endif %} + +{% if journals.count > 0 %} @@ -130,3 +137,4 @@ var edit_selected_txt = "{{ 'edit_selected'|_ }}"; var delete_selected_txt = "{{ 'delete_selected'|_ }}"; +{% endif %} \ No newline at end of file diff --git a/resources/views/transactions/index.twig b/resources/views/transactions/index.twig index bf1f2efe79..ab081e5514 100644 --- a/resources/views/transactions/index.twig +++ b/resources/views/transactions/index.twig @@ -12,7 +12,7 @@

{{ subTitle }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}