diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 896b7f36a7..a1f5c1e854 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -10,6 +10,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; +use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -61,6 +62,8 @@ class JournalCollector private $joinedBudget = false; /** @var bool */ private $joinedCategory = false; + /** @var bool */ + private $joinedTag = false; /** @var int */ private $limit; /** @var int */ @@ -116,19 +119,7 @@ class JournalCollector { $this->run = true; $set = $this->query->get(array_values($this->fields)); - - // filter out transfers: - if ($this->filterTransfers) { - $set = $set->filter( - function (Transaction $transaction) { - if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { - return $transaction; - } - - return false; - } - ); - } + $set = $this->filterTransfers($set); // loop for decryption. $set->each( @@ -184,7 +175,7 @@ class JournalCollector { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class, [$this->user]); - $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::CASH]); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); @@ -320,6 +311,19 @@ class JournalCollector return $this; } + /** + * @param Tag $tag + * + * @return JournalCollector + */ + public function setTag(Tag $tag): JournalCollector + { + $this->joinTagTables(); + $this->query->where('tag_transaction_journal.tag_id', $tag->id); + + return $this; + } + /** * @param array $types * @@ -368,6 +372,54 @@ class JournalCollector return $this; } + /** + * If the set of accounts used by the collector includes more than one asset + * account, chances are the set include double entries: transfers get selected + * on both the source, and then again on the destination account. + * + * This method filters them out. + * + * @param Collection $set + * + * @return Collection + */ + private function filterTransfers(Collection $set): Collection + { + if ($this->filterTransfers) { + $set = $set->filter( + function (Transaction $transaction) { + if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { + Log::debug( + sprintf( + 'Included journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return $transaction; + } + + Log::debug( + sprintf( + 'Removed journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return false; + } + ); + } + + return $set; + } + /** * */ @@ -394,6 +446,18 @@ class JournalCollector } } + /** + * + */ + private function joinTagTables() + { + if (!$this->joinedTag) { + // join some extra tables: + $this->joinedTag = true; + $this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + } + } + /** * @return EloquentBuilder */ diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e5ac8095e2..750e90d53a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -221,7 +221,7 @@ class AccountController extends Controller $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // replace with journal collector: @@ -275,8 +275,8 @@ class AccountController extends Controller } /** - * @param Account $account - * @param string $date + * @param Account $account + * @param string $date * * @return View */ @@ -287,8 +287,7 @@ class AccountController extends Controller $start = Navigation::startOfPeriod($carbon, $range); $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; - $page = intval(Input::get('page')); - $page = $page === 0 ? 1 : $page; + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // replace with journal collector: diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index f95a1a968b..7068130116 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -184,7 +184,7 @@ class CategoryController extends Controller $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $hideCategory = true; // used in list. - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; @@ -253,7 +253,7 @@ class CategoryController extends Controller $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $category->name; $hideCategory = true; // used in list. - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // new collector: diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 3f55f44e35..98443574d2 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -306,7 +306,7 @@ class RuleController extends Controller } // Return json response - $view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); + $view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render(); return Response::json(['html' => $view, 'warning' => $warning]); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index fc7013d41a..7a790c284a 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -13,10 +13,11 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Models\Preference; use FireflyIII\Models\Tag; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Input; @@ -226,19 +227,27 @@ class TagController extends Controller } /** - * @param Tag $tag - * @param TagRepositoryInterface $repository + * @param Tag $tag * * @return View */ - public function show(Tag $tag, TagRepositoryInterface $repository) + public function show(Tag $tag) { $subTitle = $tag->tag; $subTitleIcon = 'fa-tag'; - $journals = $repository->getJournals($tag); - $sum = $journals->sum( - function (TransactionJournal $journal) { - return TransactionJournal::amount($journal); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // use collector: + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag); + $journals = $collector->getPaginatedJournals(); + $journals->setPath('tags/show/' . $tag->id); + + $sum = $journals->sum( + function (Transaction $transaction) { + return $transaction->transaction_amount; } ); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 517ceedd7b..f1f65eebba 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -61,7 +61,7 @@ class TransactionController extends Controller $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $collector = new JournalCollector(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); diff --git a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php index 8048fe9cd7..fec910a6b3 100644 --- a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php +++ b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php @@ -14,8 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Jobs; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Contracts\Queue\ShouldQueue; @@ -129,16 +129,16 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue public function handle() { // Lookup all journals that match the parameters specified - $journals = $this->collectJournals(); + $transactions = $this->collectJournals(); // Find processors for each rule within the current rule group $processors = $this->collectProcessors(); // Execute the rules for each transaction - foreach ($journals as $journal) { + foreach ($transactions as $transaction) { /** @var Processor $processor */ foreach ($processors as $processor) { - $processor->handleTransactionJournal($journal); + $processor->handleTransaction($transaction); // Stop processing this group if the rule specifies 'stop_processing' if ($processor->getRule()->stop_processing) { @@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue */ protected function collectJournals() { - /** @var JournalTaskerInterface $tasker */ - $tasker = app(JournalTaskerInterface::class); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); - return $tasker->getJournalsInRange($this->accounts, $this->startDate, $this->endDate); + return $collector->getJournals(); } /** diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 26e3c63274..862bafadb8 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -46,73 +46,6 @@ class JournalTasker implements JournalTaskerInterface $this->user = $user; } - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator - { - $offset = ($page - 1) * $pageSize; - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - if (count($types) > 0) { - $query->transactionTypes($types); - } - $count = $this->user->transactionJournals()->transactionTypes($types)->count(); - $set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields()); - $journals = new LengthAwarePaginator($set, $count, $pageSize, $page); - - return $journals; - } - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection - { - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - $query->before($end); - $query->after($start); - - if ($accounts->count() > 0) { - $ids = $accounts->pluck('id')->toArray(); - // join source and destination: - $query->leftJoin( - 'transactions as source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); - } - ); - $query->leftJoin( - 'transactions as destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); - } - ); - - $query->where( - function (Builder $q) use ($ids) { - $q->whereIn('destination.account_id', $ids); - $q->orWhereIn('source.account_id', $ids); - } - ); - } - - $set = $query->get(TransactionJournal::queryFields()); - - return $set; - } - /** * @param TransactionJournal $journal * diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index 89ae1c8289..09ad5813a7 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -26,27 +26,6 @@ use Illuminate\Support\Collection; */ interface JournalTaskerInterface { - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator; - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection; /** * @param TransactionJournal $journal diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 6c29921d88..c71d031d72 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -133,24 +133,6 @@ class TagRepository implements TagRepositoryInterface return $tags; } - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection - { - /** @var Collection $journals */ - $journals = $tag - ->transactionJournals() - ->sortCorrectly() - ->expanded() - ->groupBy(['tag_transaction_journal.tag_id', 'tag_transaction_journal.transaction_journal_id']) - ->get(TransactionJournal::queryFields()); - - return $journals; - } - /** * @param array $data * diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index b1676b2f03..dd1966bd51 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -65,13 +65,6 @@ interface TagRepositoryInterface */ public function get(): Collection; - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection; - /** * This method stores a tag. * diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index d7192cddf9..9b0daf0ae9 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -16,6 +16,7 @@ namespace FireflyIII\Rules; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Actions\ActionInterface; use FireflyIII\Rules\Factory\ActionFactory; @@ -144,6 +145,36 @@ final class Processor return $this->rule; } + /** + * This method will scan the given transaction journal and check if it matches the triggers found in the Processor + * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal + * matches all of the triggers (regardless of whether the Processor could act on it). + * + * @param Transaction $transaction + * + * @return bool + */ + public function handleTransaction(Transaction $transaction): bool + { + Log::debug(sprintf('handleTransactionJournal for journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); + + // grab the actual journal. + $journal = $transaction->transactionJournal()->first(); + $this->journal = $journal; + // get all triggers: + $triggered = $this->triggered(); + if ($triggered) { + if ($this->actions->count() > 0) { + $this->actions(); + } + + return true; + } + + return false; + + } + /** * This method will scan the given transaction journal and check if it matches the triggers found in the Processor * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index 7491bbbb88..eb845dee29 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -13,7 +13,8 @@ declare(strict_types = 1); namespace FireflyIII\Rules; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Support\Collection; @@ -62,7 +63,7 @@ class TransactionMatcher if (count($this->triggers) === 0) { return new Collection; } - $pagesize = min($this->range / 2, $this->limit * 2); + $pageSize = min($this->range / 2, $this->limit * 2); // Variables used within the loop $processed = 0; @@ -76,31 +77,42 @@ class TransactionMatcher // - the maximum number of transactions to search in have been searched do { // Fetch a batch of transactions from the database - $paginator = $this->tasker->getJournals($this->transactionTypes, $page, $pagesize); - $set = $paginator->getCollection(); - + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize * 2)->setPage($page)->setTypes($this->transactionTypes); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); // Filter transactions that match the given triggers. $filtered = $set->filter( - function (TransactionJournal $journal) use ($processor) { - Log::debug(sprintf('Test these triggers on #%d', $journal->id)); + function (Transaction $transaction) use ($processor) { + Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); - return $processor->handleTransactionJournal($journal); + return $processor->handleTransaction($transaction); } ); + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + // merge: /** @var Collection $result */ $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); // Update counters $page++; $processed += count($set); + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + // Check for conditions to finish the loop - $reachedEndOfList = $set->count() < $pagesize; + $reachedEndOfList = $set->count() < $pageSize; $foundEnough = $result->count() >= $this->limit; $searchedEnough = ($processed >= $this->range); + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true))); + } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); // If the list of matchingTransactions is larger than the maximum number of results diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index 47c7f5c54b..fbdc4432d4 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -95,7 +95,7 @@
- {% include 'list/journals.twig' %} + {% include 'list/journals-tasker' %}