diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 11e0e0f986..966e2905df 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -100,6 +100,7 @@ class OperatorQuerySearch implements SearchInterface private Collection $modifiers; // obsolete private Collection $operators; private string $originalQuery; + private Carbon $date; /** * OperatorQuerySearch constructor. @@ -115,6 +116,7 @@ class OperatorQuerySearch implements SearchInterface $this->words = []; $this->limit = 25; $this->originalQuery = ''; + $this->date = today(config('app.timezone')); $this->validOperators = array_keys(config('firefly.search.operators')); $this->startTime = microtime(true); $this->accountRepository = app(AccountRepositoryInterface::class); @@ -144,6 +146,14 @@ class OperatorQuerySearch implements SearchInterface return $this->operators; } + /** + * @param Carbon $date + */ + public function setDate(Carbon $date): void + { + $this->date = $date; + } + /** * @inheritDoc * @codeCoverageIgnore @@ -255,7 +265,7 @@ class OperatorQuerySearch implements SearchInterface throw new FireflyException(sprintf('Firefly III search cant handle "%s"-nodes', $class)); case Subquery::class: // loop all notes in subquery: - foreach($searchNode->getNodes() as $subNode) { + foreach ($searchNode->getNodes() as $subNode) { $this->handleSearchNode($subNode); // lets hope its not too recursive! } break; @@ -831,7 +841,7 @@ class OperatorQuerySearch implements SearchInterface { $parser = new ParseDateString; if ($parser->isDateRange($value)) { - return $parser->parseRange($value, today(config('app.timezone'))); + return $parser->parseRange($value, $this->date); } $date = $parser->parseDate($value); diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index 42a2d387b4..0dcfcc0f7d 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Search; +use Carbon\Carbon; use FireflyIII\User; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -80,4 +81,9 @@ interface SearchInterface * @param User $user */ public function setUser(User $user); + + /** + * @param Carbon $date + */ + public function setDate(Carbon $date): void; } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index e3fe50d526..0a95beb8c3 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -43,11 +43,13 @@ declare(strict_types=1); namespace FireflyIII\TransactionRules\Engine; +use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Search\SearchInterface; use FireflyIII\TransactionRules\Factory\ActionFactory; use FireflyIII\User; @@ -274,8 +276,9 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id)); $result = $collection->count() > 0; - if(true === $result) { + if (true === $result) { Log::debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count())); + return true; } Log::debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count())); @@ -332,15 +335,21 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); $searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']); } + $date = today(config('app.timezone')); + if ($this->hasSpecificJournalTrigger($searchArray)) { + $date = $this->setDateFromJournalTrigger($searchArray); + } + // build and run the search engine. $searchEngine = app(SearchInterface::class); $searchEngine->setUser($this->user); $searchEngine->setPage(1); $searchEngine->setLimit(31337); + $searchEngine->setDate($date); foreach ($searchArray as $type => $searches) { - foreach($searches as $value) { + foreach ($searches as $value) { $searchEngine->parseQuery(sprintf('%s:%s', $type, $value)); } } @@ -422,6 +431,38 @@ class SearchRuleEngine implements RuleEngineInterface return $unique; } + /** + * Search in the triggers of this particular search and if it contains + * one search operator for "journal_id" it means the date ranges + * in the search may need to be updated. + * + * @param array $array + * + * @return bool + */ + private function hasSpecificJournalTrigger(array $array): bool + { + Log::debug('Now in hasSpecificJournalTrigger.'); + $journalTrigger = false; + $dateTrigger = false; + foreach ($array as $triggerName => $values) { + if ('journal_id' === $triggerName) { + if (is_array($values) && 1 === count($values)) { + Log::debug('Found a journal_id trigger with 1 journal, true.'); + $journalTrigger = true; + } + } + if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) { + Log::debug('Found a date related trigger, set to true.'); + $dateTrigger = true; + } + } + $result = $journalTrigger && $dateTrigger; + Log::debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true))); + + return $result; + } + /** * @param RuleGroup $group * @@ -447,4 +488,36 @@ class SearchRuleEngine implements RuleEngineInterface return $all; } + + /** + * @param array $array + * + * @return Carbon + */ + private function setDateFromJournalTrigger(array $array): Carbon + { + Log::debug('Now in setDateFromJournalTrigger()'); + $journalId = 0; + foreach ($array as $triggerName => $values) { + if ('journal_id' === $triggerName) { + if (is_array($values) && 1 === count($values)) { + $journalId = (int)trim(($values[0] ?? '"0"'), '"'); // follows format "123". + Log::debug(sprintf('Found journal ID #%d', $journalId)); + } + } + } + if (0 !== $journalId) { + $repository = app(JournalRepositoryInterface::class); + $journal = $repository->findNull($journalId); + if (null !== $journal) { + $date = $journal->date; + Log::debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d'))); + + return $date; + } + } + Log::debug('Found no journal, return default date.'); + + return today(config('app.timezone')); + } }