mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-08-28 17:07:21 +00:00
Expand search.
This commit is contained in:
@@ -98,6 +98,7 @@ class ApplyRules extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
$this->stupidLaravel();
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$this->verifyAccessToken()) {
|
||||
@@ -152,6 +153,8 @@ class ApplyRules extends Command
|
||||
$ruleEngine->setUser($this->getUser());
|
||||
$ruleEngine->setRulesToApply($rulesToApply);
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
|
||||
// for this call, the rule engine only includes "store" rules:
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
|
||||
@@ -166,9 +169,9 @@ class ApplyRules extends Command
|
||||
$bar->advance();
|
||||
}
|
||||
$this->line('');
|
||||
$this->line('Done!');
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->line(sprintf('Done in %s seconds!', $end));
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -56,9 +56,6 @@ class AccountFactory
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
$this->canHaveVirtual = [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD];
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->validAssetFields = ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'];
|
||||
|
@@ -34,18 +34,6 @@ use Log;
|
||||
*/
|
||||
class AccountMetaFactory
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
|
@@ -36,21 +36,7 @@ use Log;
|
||||
*/
|
||||
class AttachmentFactory
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
@@ -62,7 +62,7 @@ trait AmountCollection
|
||||
{
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($amount) {
|
||||
$q->where('destination.amount', '<', app('steam')->positive($amount));
|
||||
$q->where('destination.amount', '<=', app('steam')->positive($amount));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ trait AmountCollection
|
||||
{
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($amount) {
|
||||
$q->where('destination.amount', '>', app('steam')->positive($amount));
|
||||
$q->where('destination.amount', '>=', app('steam')->positive($amount));
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -61,6 +61,73 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesContain(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes', 'LIKE', sprintf('%%%s%%', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesEndWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes', 'LIKE', sprintf('%%%s', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutNotes(): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->whereNull('notes');
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withAnyNotes(): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->whereNotNull('notes');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesExactly(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes', '=', sprintf('%s', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesStartWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes', 'LIKE', sprintf('%s%%', $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific bill.
|
||||
*
|
||||
@@ -185,6 +252,32 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where has no tags.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutTags(): GroupCollectorInterface
|
||||
{
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNull('tag_transaction_journal.tag_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where has no tags.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAnyTag(): GroupCollectorInterface
|
||||
{
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNotNull('tag_transaction_journal.tag_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will include bill name + ID, if any.
|
||||
*
|
||||
@@ -272,11 +365,20 @@ trait MetaCollection
|
||||
public function withoutBudget(): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('budget_transaction_journal.budget_id');
|
||||
}
|
||||
);
|
||||
$this->query->whereNull('budget_transaction_journal.budget_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a transactions without a budget..
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withBudget(): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
$this->query->whereNotNull('budget_transaction_journal.budget_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -289,11 +391,20 @@ trait MetaCollection
|
||||
public function withoutCategory(): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('category_transaction_journal.category_id');
|
||||
}
|
||||
);
|
||||
$this->query->whereNull('category_transaction_journal.category_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a transactions without a category.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withCategory(): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
$this->query->whereNotNull('category_transaction_journal.category_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@@ -130,7 +130,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function dumpQuery(): void
|
||||
{
|
||||
echo $this->query->toSql();
|
||||
echo $this->query->select($this->fields)->toSql();
|
||||
echo '<pre>';
|
||||
print_r($this->query->getBindings());
|
||||
echo '</pre>';
|
||||
@@ -232,6 +232,16 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.foreign_currency_id', $currency->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a specific transaction group.
|
||||
*
|
||||
@@ -326,6 +336,79 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionStarts(array $array): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array) {
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%s%%', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%s%%', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionEnds(array $array): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array) {
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionIs(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($value) {
|
||||
$q->where('transaction_journals.description', '=', $value);
|
||||
$q->orWhere('transaction_groups.title', '=', $value);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
@@ -417,6 +500,20 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has attachments
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAttachments(): GroupCollectorInterface
|
||||
{
|
||||
Log::debug('Add filter on attachment ID.');
|
||||
$this->joinAttachmentTables();
|
||||
$this->query->whereNotNull('attachments.attachable_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get attachment information.
|
||||
*/
|
||||
@@ -655,7 +752,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as source',
|
||||
function (JoinClause $join) {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('source.amount', '<', 0);
|
||||
->where('source.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
// join destination transaction
|
||||
@@ -663,7 +760,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as destination',
|
||||
function (JoinClause $join) {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('destination.amount', '>', 0);
|
||||
->where('destination.amount', '>', 0);
|
||||
}
|
||||
)
|
||||
// left join transaction type.
|
||||
|
@@ -220,6 +220,15 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a specific foreign currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Set destination accounts.
|
||||
*
|
||||
@@ -284,6 +293,33 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setSearchWords(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Beginning of the description must match:
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionStarts(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* End of the description must match:
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionEnds(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Description must be:
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionIs(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Set source accounts.
|
||||
*
|
||||
@@ -311,6 +347,16 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setTags(Collection $tags): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutTags(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAnyTag(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
*
|
||||
@@ -377,6 +423,13 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withAttachmentInformation(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Has attachments
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAttachments(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Include bill name + ID.
|
||||
*
|
||||
@@ -405,6 +458,42 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Any notes, no matter what.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withAnyNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesContain(string $value): GroupCollectorInterface;
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesStartWith(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesEndWith(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesExactly(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Add tag info.
|
||||
*
|
||||
@@ -426,6 +515,20 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withoutCategory(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a transactions with a category.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withCategory(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a transactions with a budget.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withBudget(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Look for specific external ID's.
|
||||
*
|
||||
|
@@ -38,6 +38,7 @@ use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Update\AccountUpdateService;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use \Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Storage;
|
||||
@@ -346,7 +347,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
if (1 === $result->count()) {
|
||||
return (string)$result->first()->data;
|
||||
return (string) $result->first()->data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -704,4 +705,38 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection
|
||||
{
|
||||
$dbQuery = $this->user->accounts()->distinct()
|
||||
->leftJoin('account_meta', 'accounts.id', 'account_meta.account_id')
|
||||
->where('accounts.active', 1)
|
||||
->orderBy('accounts.order', 'ASC')
|
||||
->orderBy('accounts.account_type_id', 'ASC')
|
||||
->orderBy('accounts.name', 'ASC')
|
||||
->with(['accountType', 'accountMeta']);
|
||||
if ('' !== $query) {
|
||||
// split query on spaces just in case:
|
||||
$parts = explode(' ', $query);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$dbQuery->where(function (EloquentBuilder $q1) use ($search) {
|
||||
$q1->where('accounts.iban', 'LIKE', $search);
|
||||
$q1->orWhere(function (EloquentBuilder $q2) use ($search) {
|
||||
$q2->where('account_meta.name', '=', 'account_number');
|
||||
$q2->where('account_meta.data', 'LIKE', $search);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
if (count($types) > 0) {
|
||||
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$dbQuery->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
return $dbQuery->take($limit)->get(['accounts.*']);
|
||||
}
|
||||
}
|
||||
|
@@ -289,6 +289,15 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $types
|
||||
* @param int $limit
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
|
@@ -47,18 +47,7 @@ use Log;
|
||||
*/
|
||||
class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
|
@@ -25,12 +25,16 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Gdbots\QueryParser\Node\Field;
|
||||
use Gdbots\QueryParser\Node\Node;
|
||||
@@ -47,19 +51,22 @@ use Log;
|
||||
*/
|
||||
class BetterQuerySearch implements SearchInterface
|
||||
{
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private BillRepositoryInterface $billRepository;
|
||||
private BudgetRepositoryInterface $budgetRepository;
|
||||
private CategoryRepositoryInterface $categoryRepository;
|
||||
private TagRepositoryInterface $tagRepository;
|
||||
private User $user;
|
||||
private ParsedQuery $query;
|
||||
private int $page;
|
||||
private array $words;
|
||||
private array $validOperators;
|
||||
private GroupCollectorInterface $collector;
|
||||
private float $startTime;
|
||||
private Collection $modifiers;
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
private BillRepositoryInterface $billRepository;
|
||||
private BudgetRepositoryInterface $budgetRepository;
|
||||
private CategoryRepositoryInterface $categoryRepository;
|
||||
private TagRepositoryInterface $tagRepository;
|
||||
private CurrencyRepositoryInterface $currencyRepository;
|
||||
private TransactionTypeRepositoryInterface $typeRepository;
|
||||
private User $user;
|
||||
private ParsedQuery $query;
|
||||
private int $page;
|
||||
private array $words;
|
||||
private array $validOperators;
|
||||
private GroupCollectorInterface $collector;
|
||||
private float $startTime;
|
||||
private Collection $modifiers; // obsolete
|
||||
private Collection $operators;
|
||||
|
||||
/**
|
||||
* BetterQuerySearch constructor.
|
||||
@@ -68,7 +75,8 @@ class BetterQuerySearch implements SearchInterface
|
||||
public function __construct()
|
||||
{
|
||||
Log::debug('Constructed BetterQuerySearch');
|
||||
$this->modifiers = new Collection;
|
||||
$this->modifiers = new Collection; // obsolete
|
||||
$this->operators = new Collection;
|
||||
$this->page = 1;
|
||||
$this->words = [];
|
||||
$this->validOperators = array_keys(config('firefly.search.operators'));
|
||||
@@ -78,6 +86,8 @@ class BetterQuerySearch implements SearchInterface
|
||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->tagRepository = app(TagRepositoryInterface::class);
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->typeRepository = app(TransactionTypeRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +96,16 @@ class BetterQuerySearch implements SearchInterface
|
||||
*/
|
||||
public function getModifiers(): Collection
|
||||
{
|
||||
return $this->modifiers;
|
||||
die(__METHOD__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function getOperators(): Collection
|
||||
{
|
||||
return $this->operators;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,6 +117,14 @@ class BetterQuerySearch implements SearchInterface
|
||||
return implode(' ', $this->words);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWords(): array
|
||||
{
|
||||
return $this->words;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @codeCoverageIgnore
|
||||
@@ -154,9 +181,13 @@ class BetterQuerySearch implements SearchInterface
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function searchTransactions(): LengthAwarePaginator
|
||||
{
|
||||
if (0 === count($this->getWords()) && 0 === count($this->getOperators())) {
|
||||
throw new FireflyException('Search query is empty.');
|
||||
}
|
||||
return $this->collector->getPaginatedGroups();
|
||||
}
|
||||
|
||||
@@ -197,11 +228,14 @@ class BetterQuerySearch implements SearchInterface
|
||||
$value = $searchNode->getNode()->getValue();
|
||||
// must be valid operator:
|
||||
if (in_array($operator, $this->validOperators, true)) {
|
||||
$this->updateCollector($operator, $value);
|
||||
$this->modifiers->push([
|
||||
'type' => $operator,
|
||||
'value' => $value,
|
||||
]);
|
||||
if ($this->updateCollector($operator, $value)) {
|
||||
$this->operators->push(
|
||||
[
|
||||
'type' => $operator,
|
||||
'value' => $value,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -211,12 +245,16 @@ class BetterQuerySearch implements SearchInterface
|
||||
/**
|
||||
* @param string $operator
|
||||
* @param string $value
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateCollector(string $operator, string $value): void
|
||||
private function updateCollector(string $operator, string $value): bool
|
||||
{
|
||||
Log::debug(sprintf('updateCollector(%s, %s)', $operator, $value));
|
||||
$allAccounts = new Collection;
|
||||
|
||||
// check if alias, replace if necessary:
|
||||
$operator = $this->getRootOperator($operator);
|
||||
|
||||
switch ($operator) {
|
||||
default:
|
||||
Log::error(sprintf('No such operator: %s', $operator));
|
||||
@@ -224,94 +262,228 @@ class BetterQuerySearch implements SearchInterface
|
||||
// some search operators are ignored, basically:
|
||||
case 'user_action':
|
||||
Log::info(sprintf('Ignore search operator "%s"', $operator));
|
||||
return false;
|
||||
//
|
||||
// all account related searches:
|
||||
//
|
||||
case 'source_account_starts':
|
||||
$this->searchAccount($value, 1, 1);
|
||||
break;
|
||||
case 'from_account_starts':
|
||||
$this->fromAccountStarts($value);
|
||||
case 'source_account_ends':
|
||||
$this->searchAccount($value, 1, 2);
|
||||
break;
|
||||
case 'from_account_ends':
|
||||
$this->fromAccountEnds($value);
|
||||
case 'source_account_is':
|
||||
$this->searchAccount($value, 1, 4);
|
||||
break;
|
||||
case 'from_account_contains':
|
||||
case 'from':
|
||||
case 'source':
|
||||
// source can only be asset, liability or revenue account:
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 25);
|
||||
if ($accounts->count() > 0) {
|
||||
$allAccounts = $accounts->merge($allAccounts);
|
||||
case 'source_account_nr_starts':
|
||||
$this->searchAccountNr($value, 1, 1);
|
||||
break;
|
||||
case 'source_account_nr_ends':
|
||||
$this->searchAccountNr($value, 1, 2);
|
||||
break;
|
||||
case 'source_account_nr_is':
|
||||
$this->searchAccountNr($value, 1, 4);
|
||||
break;
|
||||
case 'source_account_nr_contains':
|
||||
$this->searchAccountNr($value, 1, 3);
|
||||
break;
|
||||
case 'source_account_contains':
|
||||
$this->searchAccount($value, 1, 3);
|
||||
break;
|
||||
case 'source_account_id':
|
||||
$account = $this->accountRepository->findNull((int)$value);
|
||||
if(null !== $account) {
|
||||
$this->collector->setSourceAccounts(new Collection([$account]));
|
||||
}
|
||||
$this->collector->setSourceAccounts($allAccounts);
|
||||
break;
|
||||
case 'to':
|
||||
case 'destination':
|
||||
// source can only be asset, liability or expense account:
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE];
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 25);
|
||||
if ($accounts->count() > 0) {
|
||||
$allAccounts = $accounts->merge($allAccounts);
|
||||
case 'destination_account_starts':
|
||||
$this->searchAccount($value, 2, 1);
|
||||
break;
|
||||
case 'destination_account_ends':
|
||||
$this->searchAccount($value, 2, 2);
|
||||
break;
|
||||
case 'destination_account_nr_starts':
|
||||
$this->searchAccountNr($value, 2, 1);
|
||||
break;
|
||||
case 'destination_account_nr_ends':
|
||||
$this->searchAccountNr($value, 2, 2);
|
||||
break;
|
||||
case 'destination_account_nr_is':
|
||||
$this->searchAccountNr($value, 2, 4);
|
||||
break;
|
||||
case 'destination_account_is':
|
||||
$this->searchAccount($value, 2, 4);
|
||||
break;
|
||||
case 'destination_account_nr_contains':
|
||||
$this->searchAccountNr($value, 2, 3);
|
||||
break;
|
||||
case 'destination_account_contains':
|
||||
$this->searchAccount($value, 2, 3);
|
||||
break;
|
||||
case 'destination_account_id':
|
||||
$account = $this->accountRepository->findNull((int)$value);
|
||||
if(null !== $account) {
|
||||
$this->collector->setDestinationAccounts(new Collection([$account]));
|
||||
}
|
||||
$this->collector->setDestinationAccounts($allAccounts);
|
||||
break;
|
||||
case 'category':
|
||||
case 'account_id':
|
||||
$account = $this->accountRepository->findNull((int)$value);
|
||||
if(null !== $account) {
|
||||
$this->collector->setAccounts(new Collection([$account]));
|
||||
}
|
||||
break;
|
||||
//
|
||||
// description
|
||||
//
|
||||
case 'description_starts':
|
||||
$this->collector->descriptionStarts([$value]);
|
||||
break;
|
||||
case 'description_ends':
|
||||
$this->collector->descriptionEnds([$value]);
|
||||
break;
|
||||
case 'description_contains':
|
||||
$this->words[] = $value;
|
||||
return false;
|
||||
case 'description_is':
|
||||
$this->collector->descriptionIs($value);
|
||||
break;
|
||||
//
|
||||
// currency
|
||||
//
|
||||
case 'currency_is':
|
||||
$currency = $this->findCurrency($value);
|
||||
if (null !== $currency) {
|
||||
$this->collector->setCurrency($currency);
|
||||
}
|
||||
break;
|
||||
case 'foreign_currency_is':
|
||||
$currency = $this->findCurrency($value);
|
||||
if (null !== $currency) {
|
||||
$this->collector->setForeignCurrency($currency);
|
||||
}
|
||||
break;
|
||||
//
|
||||
// attachments
|
||||
//
|
||||
case 'has_attachments':
|
||||
Log::debug('Set collector to filter on attachments.');
|
||||
$this->collector->hasAttachments();
|
||||
break;
|
||||
//
|
||||
// categories
|
||||
case 'has_no_category':
|
||||
$this->collector->withoutCategory();
|
||||
break;
|
||||
case 'has_any_category':
|
||||
$this->collector->withCategory();
|
||||
break;
|
||||
case 'category_is':
|
||||
$result = $this->categoryRepository->searchCategory($value, 25);
|
||||
if ($result->count() > 0) {
|
||||
$this->collector->setCategories($result);
|
||||
}
|
||||
break;
|
||||
//
|
||||
// budgets
|
||||
//
|
||||
case 'has_no_budget':
|
||||
$this->collector->withoutBudget();
|
||||
break;
|
||||
case 'has_any_budget':
|
||||
$this->collector->withBudget();
|
||||
break;
|
||||
case 'budget':
|
||||
case 'budget_is':
|
||||
$result = $this->budgetRepository->searchBudget($value, 25);
|
||||
if ($result->count() > 0) {
|
||||
$this->collector->setBudgets($result);
|
||||
}
|
||||
break;
|
||||
//
|
||||
// bill
|
||||
//
|
||||
case 'bill':
|
||||
case 'bill_is':
|
||||
$result = $this->billRepository->searchBill($value, 25);
|
||||
if ($result->count() > 0) {
|
||||
$this->collector->setBills($result);
|
||||
}
|
||||
break;
|
||||
//
|
||||
// tags
|
||||
//
|
||||
case 'has_no_tag':
|
||||
$this->collector->withoutTags();
|
||||
break;
|
||||
case 'has_any_tag':
|
||||
$this->collector->hasAnyTag();
|
||||
break;
|
||||
case 'tag':
|
||||
$result = $this->tagRepository->searchTag($value);
|
||||
if ($result->count() > 0) {
|
||||
$this->collector->setTags($result);
|
||||
}
|
||||
break;
|
||||
case 'budget':
|
||||
$result = $this->budgetRepository->searchBudget($value, 25);
|
||||
if ($result->count() > 0) {
|
||||
$this->collector->setBudgets($result);
|
||||
}
|
||||
//
|
||||
// notes
|
||||
//
|
||||
case 'notes_contain':
|
||||
$this->collector->notesContain($value);
|
||||
break;
|
||||
case 'amount_is':
|
||||
case 'amount':
|
||||
case 'notes_start':
|
||||
$this->collector->notesStartWith($value);
|
||||
break;
|
||||
case 'notes_end':
|
||||
$this->collector->notesEndWith($value);
|
||||
break;
|
||||
case 'notes_are':
|
||||
$this->collector->notesExactly($value);
|
||||
break;
|
||||
case 'no_notes':
|
||||
$this->collector->withoutNotes();
|
||||
break;
|
||||
case 'any_notes':
|
||||
$this->collector->withAnyNotes();
|
||||
break;
|
||||
//
|
||||
// amount
|
||||
//
|
||||
case 'amount_exactly':
|
||||
$amount = app('steam')->positive((string) $value);
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
|
||||
$this->collector->amountIs($amount);
|
||||
break;
|
||||
case 'amount_max':
|
||||
case 'amount_less':
|
||||
$amount = app('steam')->positive((string) $value);
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
|
||||
$this->collector->amountLess($amount);
|
||||
break;
|
||||
case 'amount_min':
|
||||
case 'amount_more':
|
||||
$amount = app('steam')->positive((string) $value);
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
|
||||
$this->collector->amountMore($amount);
|
||||
break;
|
||||
case 'type':
|
||||
//
|
||||
// transaction type
|
||||
//
|
||||
case 'transaction_type':
|
||||
$this->collector->setTypes([ucfirst($value)]);
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
break;
|
||||
case 'date':
|
||||
case 'on':
|
||||
//
|
||||
// dates
|
||||
//
|
||||
case 'date_is':
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
$start = new Carbon($value);
|
||||
$this->collector->setRange($start, $start);
|
||||
break;
|
||||
case 'date_before':
|
||||
case 'before':
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
$before = new Carbon($value);
|
||||
$this->collector->setBefore($before);
|
||||
break;
|
||||
case 'date_after':
|
||||
case 'after':
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
$after = new Carbon($value);
|
||||
$this->collector->setAfter($after);
|
||||
@@ -326,6 +498,9 @@ class BetterQuerySearch implements SearchInterface
|
||||
$updatedAt = new Carbon($value);
|
||||
$this->collector->setUpdatedAt($updatedAt);
|
||||
break;
|
||||
//
|
||||
// other fields
|
||||
//
|
||||
case 'external_id':
|
||||
$this->collector->setExternalId($value);
|
||||
break;
|
||||
@@ -333,55 +508,159 @@ class BetterQuerySearch implements SearchInterface
|
||||
$this->collector->setInternalReference($value);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* searchDirection: 1 = source (default), 2 = destination
|
||||
* stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is
|
||||
* @param string $value
|
||||
* @param int $searchDirection
|
||||
* @param int $stringPosition
|
||||
*/
|
||||
private function fromAccountStarts(string $value): void
|
||||
private function searchAccount(string $value, int $searchDirection, int $stringPosition): void
|
||||
{
|
||||
Log::debug(sprintf('fromAccountStarts(%s)', $value));
|
||||
// source can only be asset, liability or revenue account:
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 25);
|
||||
Log::debug(sprintf('searchAccount(%s, %d, %d)', $value, $stringPosition, $searchDirection));
|
||||
|
||||
// search direction (default): for source accounts
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||
$collectorMethod = 'setSourceAccounts';
|
||||
|
||||
// search direction: for destination accounts
|
||||
if (2 === $searchDirection) {
|
||||
// destination can be
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE];
|
||||
$collectorMethod = 'setDestinationAccounts';
|
||||
}
|
||||
// string position (default): starts with:
|
||||
$stringMethod = 'str_starts_with';
|
||||
|
||||
// string position: ends with:
|
||||
if (2 === $stringPosition) {
|
||||
$stringMethod = 'str_ends_with';
|
||||
}
|
||||
if (3 === $stringPosition) {
|
||||
$stringMethod = 'str_contains';
|
||||
}
|
||||
if (4 === $stringPosition) {
|
||||
$stringMethod = 'str_is_equal';
|
||||
}
|
||||
|
||||
// get accounts:
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 25);
|
||||
if (0 === $accounts->count()) {
|
||||
Log::debug('Found zero, return.');
|
||||
Log::debug('Found zero accounts, do nothing.');
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Found %d, filter.', $accounts->count()));
|
||||
$filtered = $accounts->filter(function (Account $account) use ($value) {
|
||||
return str_starts_with($account->name, $value);
|
||||
Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count()));
|
||||
$filtered = $accounts->filter(function (Account $account) use ($value, $stringMethod) {
|
||||
return $stringMethod(strtolower($account->name), strtolower($value));
|
||||
});
|
||||
|
||||
if (0 === $filtered->count()) {
|
||||
Log::debug('Left with zero accounts, return.');
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Left with %d, set as %s().', $filtered->count(), $collectorMethod));
|
||||
$this->collector->$collectorMethod($filtered);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* searchDirection: 1 = source (default), 2 = destination
|
||||
* stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is
|
||||
* @param string $value
|
||||
* @param int $searchDirection
|
||||
* @param int $stringPosition
|
||||
*/
|
||||
private function searchAccountNr(string $value, int $searchDirection, int $stringPosition): void
|
||||
{
|
||||
Log::debug(sprintf('searchAccountNr(%s, %d, %d)', $value, $searchDirection, $stringPosition));
|
||||
|
||||
// search direction (default): for source accounts
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||
$collectorMethod = 'setSourceAccounts';
|
||||
|
||||
// search direction: for destination accounts
|
||||
if (2 === $searchDirection) {
|
||||
// destination can be
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE];
|
||||
$collectorMethod = 'setDestinationAccounts';
|
||||
}
|
||||
// string position (default): starts with:
|
||||
$stringMethod = 'str_starts_with';
|
||||
|
||||
// string position: ends with:
|
||||
if (2 === $stringPosition) {
|
||||
$stringMethod = 'str_ends_with';
|
||||
}
|
||||
if (3 === $stringPosition) {
|
||||
$stringMethod = 'str_contains';
|
||||
}
|
||||
if (4 === $stringPosition) {
|
||||
$stringMethod = 'str_is_equal';
|
||||
}
|
||||
|
||||
// search for accounts:
|
||||
$accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 25);
|
||||
if (0 === $accounts->count()) {
|
||||
Log::debug('Found zero accounts, do nothing.');
|
||||
return;
|
||||
}
|
||||
|
||||
// if found, do filter
|
||||
Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count()));
|
||||
$filtered = $accounts->filter(function (Account $account) use ($value, $stringMethod) {
|
||||
// either IBAN or account number!
|
||||
$ibanMatch = $stringMethod(strtolower($account->iban), strtolower($value));
|
||||
$accountNrMatch = false;
|
||||
/** @var AccountMeta $meta */
|
||||
foreach ($account->accountMeta as $meta) {
|
||||
if ('account_number' === $meta->name && $stringMethod(strtolower($meta->data), strtolower($value))) {
|
||||
$accountNrMatch = true;
|
||||
}
|
||||
}
|
||||
return $ibanMatch || $accountNrMatch;
|
||||
});
|
||||
|
||||
if (0 === $filtered->count()) {
|
||||
Log::debug('Left with zero, return.');
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Left with %d, set.', $accounts->count()));
|
||||
$this->collector->setSourceAccounts($filtered);
|
||||
Log::debug('Left with zero accounts, return.');
|
||||
$this->collector->$collectorMethod($filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
private function fromAccountEnds(string $value): void
|
||||
private function findCurrency(string $value): ?TransactionCurrency
|
||||
{
|
||||
Log::debug(sprintf('fromAccountEnds(%s)', $value));
|
||||
// source can only be asset, liability or revenue account:
|
||||
$searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE];
|
||||
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 25);
|
||||
if (0 === $accounts->count()) {
|
||||
Log::debug('Found zero, return.');
|
||||
return;
|
||||
$result = $this->currencyRepository->findByCodeNull($value);
|
||||
if (null === $result) {
|
||||
$result = $this->currencyRepository->findByNameNull($value);
|
||||
}
|
||||
Log::debug(sprintf('Found %d, filter.', $accounts->count()));
|
||||
$filtered = $accounts->filter(function (Account $account) use ($value) {
|
||||
return str_ends_with($account->name, $value);
|
||||
});
|
||||
if (0 === $filtered->count()) {
|
||||
Log::debug('Left with zero, return.');
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Left with %d, set.', $accounts->count()));
|
||||
$this->collector->setSourceAccounts($filtered);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
private function getRootOperator(string $operator): string
|
||||
{
|
||||
$config = config(sprintf('firefly.search.operators.%s', $operator));
|
||||
if (null === $config) {
|
||||
throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator));
|
||||
}
|
||||
if (true === $config['alias']) {
|
||||
Log::debug(sprintf('"%s" is an alias for "%s", so return that instead.', $operator, $config['alias_for']));
|
||||
return $config['alias_for'];
|
||||
}
|
||||
Log::debug(sprintf('"%s" is not an alias.', $operator));
|
||||
|
||||
return $operator;
|
||||
}
|
||||
|
||||
}
|
@@ -36,6 +36,11 @@ interface SearchInterface
|
||||
*/
|
||||
public function getModifiers(): Collection;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getOperators(): Collection;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@@ -41,6 +41,7 @@ use Log;
|
||||
* Set the user, then apply an array to setRulesToApply(array) or call addRuleIdToApply(int) or addRuleToApply(Rule).
|
||||
* Then call process() to make the magic happen.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
class RuleEngine
|
||||
{
|
||||
@@ -50,18 +51,12 @@ class RuleEngine
|
||||
public const TRIGGER_UPDATE = 2;
|
||||
/** @var int */
|
||||
public const TRIGGER_BOTH = 3;
|
||||
/** @var bool */
|
||||
private $allRules;
|
||||
/** @var RuleGroupRepository */
|
||||
private $ruleGroupRepository;
|
||||
/** @var Collection */
|
||||
private $ruleGroups;
|
||||
/** @var array */
|
||||
private $rulesToApply;
|
||||
/** @var int */
|
||||
private $triggerMode;
|
||||
/** @var User */
|
||||
private $user;
|
||||
private bool $allRules;
|
||||
private RuleGroupRepository $ruleGroupRepository;
|
||||
private Collection $ruleGroups;
|
||||
private array $rulesToApply;
|
||||
private int $triggerMode;
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* RuleEngine constructor.
|
||||
@@ -230,7 +225,7 @@ class RuleEngine
|
||||
|
||||
$validTrigger = ('store-journal' === $trigger->trigger_value && self::TRIGGER_STORE === $this->triggerMode)
|
||||
|| ('update-journal' === $trigger->trigger_value && self::TRIGGER_UPDATE === $this->triggerMode)
|
||||
|| $this->triggerMode === self::TRIGGER_BOTH;
|
||||
|| $this->triggerMode === self::TRIGGER_BOTH;
|
||||
|
||||
return $validTrigger && ($this->allRules || in_array($rule->id, $this->rulesToApply, true)) && true === $rule->active;
|
||||
}
|
||||
|
35
app/TransactionRules/Engine/RuleEngineInterface.php
Normal file
35
app/TransactionRules/Engine/RuleEngineInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/*
|
||||
* RuleEngineInterface.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\TransactionRules\Engine;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface RuleEngineInterface
|
||||
*/
|
||||
interface RuleEngineInterface
|
||||
{
|
||||
public function setRules(Collection $rules): void;
|
||||
|
||||
public function setRuleGroups(Collection $ruleGroups): void;
|
||||
|
||||
}
|
@@ -52,6 +52,18 @@ if (!function_exists('envNonEmpty')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('str_is_equal')) {
|
||||
/**
|
||||
* @param string $left
|
||||
* @param string $right
|
||||
* @return bool
|
||||
*/
|
||||
function str_is_equal(string $left, string $right): bool
|
||||
{
|
||||
return $left === $right;
|
||||
}
|
||||
}
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
realpath(__DIR__ . '/../')
|
||||
);
|
||||
|
@@ -92,14 +92,6 @@ use FireflyIII\TransactionRules\Triggers\DescriptionEnds;
|
||||
use FireflyIII\TransactionRules\Triggers\DescriptionIs;
|
||||
use FireflyIII\TransactionRules\Triggers\DescriptionStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\ForeignCurrencyIs;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountContains;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountEnds;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountIs;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountNumberContains;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountNumberEnds;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountNumberIs;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountNumberStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\FromAccountStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\HasAnyBudget;
|
||||
use FireflyIII\TransactionRules\Triggers\HasAnyCategory;
|
||||
use FireflyIII\TransactionRules\Triggers\HasAnyTag;
|
||||
@@ -114,14 +106,6 @@ use FireflyIII\TransactionRules\Triggers\NotesEmpty;
|
||||
use FireflyIII\TransactionRules\Triggers\NotesEnd;
|
||||
use FireflyIII\TransactionRules\Triggers\NotesStart;
|
||||
use FireflyIII\TransactionRules\Triggers\TagIs;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountContains;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountEnds;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountIs;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountNumberContains;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountNumberEnds;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountNumberIs;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountNumberStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\TransactionType;
|
||||
use FireflyIII\TransactionRules\Triggers\UserAction;
|
||||
use FireflyIII\User;
|
||||
@@ -498,95 +482,156 @@ return [
|
||||
|
||||
'search' => [
|
||||
'operators' => [
|
||||
'user_action' => ['alias' => false, 'trigger_class' => UserAction::class, 'needs_context' => true,],
|
||||
'from_account_starts' => ['alias' => false, 'trigger_class' => FromAccountStarts::class, 'needs_context' => true,],
|
||||
'from_account_ends' => ['alias' => false, 'trigger_class' => FromAccountEnds::class, 'needs_context' => true,],
|
||||
'from_account_contains' => ['alias' => false, 'trigger_class' => FromAccountContains::class, 'needs_context' => true,],
|
||||
'from_account_nr_starts' => ['alias' => false, 'trigger_class' => FromAccountNumberStarts::class, 'needs_context' => true,],
|
||||
'from_account_nr_ends' => ['alias' => false, 'trigger_class' => FromAccountNumberEnds::class, 'needs_context' => true,],
|
||||
'from_account_nr_is' => ['alias' => false, 'trigger_class' => FromAccountNumberIs::class, 'needs_context' => true,],
|
||||
'from_account_nr_contains' => ['alias' => false, 'trigger_class' => FromAccountNumberContains::class, 'needs_context' => true,],
|
||||
'to_account_starts' => ['alias' => false, 'trigger_class' => ToAccountStarts::class, 'needs_context' => true,],
|
||||
'to_account_ends' => ['alias' => false, 'trigger_class' => ToAccountEnds::class, 'needs_context' => true,],
|
||||
'to_account_contains' => ['alias' => false, 'trigger_class' => ToAccountContains::class, 'needs_context' => true,],
|
||||
'to_account_nr_starts' => ['alias' => false, 'trigger_class' => ToAccountNumberStarts::class, 'needs_context' => true,],
|
||||
'to_account_nr_ends' => ['alias' => false, 'trigger_class' => ToAccountNumberEnds::class, 'needs_context' => true,],
|
||||
'to_account_nr_is' => ['alias' => false, 'trigger_class' => ToAccountNumberIs::class, 'needs_context' => true,],
|
||||
'to_account_nr_contains' => ['alias' => false, 'trigger_class' => ToAccountNumberContains::class, 'needs_context' => true,],
|
||||
'description_starts' => ['alias' => false, 'trigger_class' => DescriptionStarts::class, 'needs_context' => true,],
|
||||
'description_ends' => ['alias' => false, 'trigger_class' => DescriptionEnds::class, 'needs_context' => true,],
|
||||
'description_contains' => ['alias' => false, 'trigger_class' => DescriptionContains::class, 'needs_context' => true,],
|
||||
'description_is' => ['alias' => false, 'trigger_class' => DescriptionIs::class, 'needs_context' => true,],
|
||||
'currency_is' => ['alias' => false, 'trigger_class' => CurrencyIs::class, 'needs_context' => true,],
|
||||
'foreign_currency_is' => ['alias' => false, 'trigger_class' => ForeignCurrencyIs::class, 'needs_context' => true,],
|
||||
'has_attachments' => ['alias' => false, 'trigger_class' => HasAttachment::class, 'needs_context' => false,],
|
||||
'has_no_category' => ['alias' => false, 'trigger_class' => HasNoCategory::class, 'needs_context' => false,],
|
||||
'has_any_category' => ['alias' => false, 'trigger_class' => HasAnyCategory::class, 'needs_context' => false,],
|
||||
'has_no_budget' => ['alias' => false, 'trigger_class' => HasNoBudget::class, 'needs_context' => false,],
|
||||
'has_any_budget' => ['alias' => false, 'trigger_class' => HasAnyBudget::class, 'needs_context' => false,],
|
||||
'has_no_tag' => ['alias' => false, 'trigger_class' => HasNoTag::class, 'needs_context' => false,],
|
||||
'has_any_tag' => ['alias' => false, 'trigger_class' => HasAnyTag::class, 'needs_context' => false,],
|
||||
'notes_contain' => ['alias' => false, 'trigger_class' => NotesContain::class, 'needs_context' => true,],
|
||||
'notes_start' => ['alias' => false, 'trigger_class' => NotesStart::class, 'needs_context' => true,],
|
||||
'notes_end' => ['alias' => false, 'trigger_class' => NotesEnd::class, 'needs_context' => true,],
|
||||
'notes_are' => ['alias' => false, 'trigger_class' => NotesAre::class, 'needs_context' => true,],
|
||||
'no_notes' => ['alias' => false, 'trigger_class' => NotesEmpty::class, 'needs_context' => false,],
|
||||
'any_notes' => ['alias' => false, 'trigger_class' => NotesAny::class, 'needs_context' => false,],
|
||||
'user_action' => ['alias' => false, 'trigger_class' => UserAction::class, 'needs_context' => true,],
|
||||
'description_starts' => ['alias' => false, 'trigger_class' => DescriptionStarts::class, 'needs_context' => true,],
|
||||
'description_ends' => ['alias' => false, 'trigger_class' => DescriptionEnds::class, 'needs_context' => true,],
|
||||
'description_contains' => ['alias' => false, 'trigger_class' => DescriptionContains::class, 'needs_context' => true,],
|
||||
'description_is' => ['alias' => false, 'trigger_class' => DescriptionIs::class, 'needs_context' => true,],
|
||||
'currency_is' => ['alias' => false, 'trigger_class' => CurrencyIs::class, 'needs_context' => true,],
|
||||
'foreign_currency_is' => ['alias' => false, 'trigger_class' => ForeignCurrencyIs::class, 'needs_context' => true,],
|
||||
'has_attachments' => ['alias' => false, 'trigger_class' => HasAttachment::class, 'needs_context' => false,],
|
||||
'has_no_category' => ['alias' => false, 'trigger_class' => HasNoCategory::class, 'needs_context' => false,],
|
||||
'has_any_category' => ['alias' => false, 'trigger_class' => HasAnyCategory::class, 'needs_context' => false,],
|
||||
'has_no_budget' => ['alias' => false, 'trigger_class' => HasNoBudget::class, 'needs_context' => false,],
|
||||
'has_any_budget' => ['alias' => false, 'trigger_class' => HasAnyBudget::class, 'needs_context' => false,],
|
||||
'has_no_tag' => ['alias' => false, 'trigger_class' => HasNoTag::class, 'needs_context' => false,],
|
||||
'has_any_tag' => ['alias' => false, 'trigger_class' => HasAnyTag::class, 'needs_context' => false,],
|
||||
'notes_contain' => ['alias' => false, 'trigger_class' => NotesContain::class, 'needs_context' => true,],
|
||||
'notes_start' => ['alias' => false, 'trigger_class' => NotesStart::class, 'needs_context' => true,],
|
||||
'notes_end' => ['alias' => false, 'trigger_class' => NotesEnd::class, 'needs_context' => true,],
|
||||
'notes_are' => ['alias' => false, 'trigger_class' => NotesAre::class, 'needs_context' => true,],
|
||||
'no_notes' => ['alias' => false, 'trigger_class' => NotesEmpty::class, 'needs_context' => false,],
|
||||
'any_notes' => ['alias' => false, 'trigger_class' => NotesAny::class, 'needs_context' => false,],
|
||||
|
||||
// exact amount
|
||||
'amount_exactly' => ['alias' => false, 'trigger_class' => AmountExactly::class, 'needs_context' => true,],
|
||||
'amount_is' => ['alias' => true, 'alias_for' => 'amount_exactly', 'needs_context' => true,],
|
||||
'amount' => ['alias' => true, 'alias_for' => 'amount_exactly', 'needs_context' => true,],
|
||||
'amount_exactly' => ['alias' => false, 'trigger_class' => AmountExactly::class, 'needs_context' => true,],
|
||||
'amount_is' => ['alias' => true, 'alias_for' => 'amount_exactly', 'needs_context' => true,],
|
||||
'amount' => ['alias' => true, 'alias_for' => 'amount_exactly', 'needs_context' => true,],
|
||||
|
||||
// is less than
|
||||
'amount_less' => ['alias' => false, 'trigger_class' => AmountLess::class, 'needs_context' => true,],
|
||||
'amount_max' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true,],
|
||||
'amount_less' => ['alias' => false, 'trigger_class' => AmountLess::class, 'needs_context' => true,],
|
||||
'amount_max' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true,],
|
||||
|
||||
// is more than
|
||||
'amount_more' => ['alias' => false, 'trigger_class' => AmountMore::class, 'needs_context' => true,],
|
||||
'amount_min' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true,],
|
||||
'amount_more' => ['alias' => false, 'trigger_class' => AmountMore::class, 'needs_context' => true,],
|
||||
'amount_min' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true,],
|
||||
|
||||
// source account
|
||||
'from_account_is' => ['alias' => false, 'trigger_class' => FromAccountIs::class, 'needs_context' => true,],
|
||||
'source' => ['alias' => true, 'alias_for' => 'from_account_is', 'needs_context' => true,],
|
||||
'from' => ['alias' => true, 'alias_for' => 'from_account_is', 'needs_context' => true,],
|
||||
// source account name is + alias:
|
||||
'source_account_is' => ['alias' => false, 'trigger_class' => SourceAccountIs::class, 'needs_context' => true,],
|
||||
'from_account_is' => ['alias' => true, 'alias_for' => 'source_account_is', 'needs_context' => true,],
|
||||
|
||||
// destination account
|
||||
'to_account_is' => ['alias' => false, 'trigger_class' => ToAccountIs::class, 'needs_context' => true,],
|
||||
'destination' => ['alias' => true, 'alias_for' => 'to_account_is', 'needs_context' => true,],
|
||||
'to' => ['alias' => true, 'alias_for' => 'to_account_is', 'needs_context' => true,],
|
||||
// source account name contains + alias
|
||||
'source_account_contains' => ['alias' => false, 'trigger_class' => SourceAccountContains::class, 'needs_context' => true,],
|
||||
'from_account_contains' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,],
|
||||
'source' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,],
|
||||
'from' => ['alias' => true, 'alias_for' => 'source_account_contains', 'needs_context' => true,],
|
||||
|
||||
// source account name starts with + alias
|
||||
'source_account_starts' => ['alias' => false, 'trigger_class' => SourceAccountStarts::class, 'needs_context' => true,],
|
||||
'from_account_starts' => ['alias' => true, 'alias_for' => 'source_account_starts', 'needs_context' => true,],
|
||||
|
||||
// source account name ends with + alias
|
||||
'source_account_ends' => ['alias' => false, 'trigger_class' => SourceAccountEnds::class, 'needs_context' => true,],
|
||||
'from_account_ends' => ['alias' => true, 'alias_for' => 'source_account_ends', 'needs_context' => true,],
|
||||
|
||||
// source account ID + alias
|
||||
'source_account_id' => ['alias' => false, 'trigger_class' => SourceAccountIdIs::class, 'needs_context' => true,],
|
||||
'from_account_id' => ['alias' => true, 'alias_for' => 'source_account_id', 'needs_context' => true,],
|
||||
|
||||
// source account number is
|
||||
'source_account_nr_is' => ['alias' => false, 'trigger_class' => SourceAccountNumberIs::class, 'needs_context' => true,],
|
||||
'from_account_nr_is' => ['alias' => true, 'alias_for' => 'source_account_nr_is', 'needs_context' => true,],
|
||||
|
||||
// source account number contains
|
||||
'source_account_nr_contains' => ['alias' => false, 'trigger_class' => SourceAccountNumberContains::class, 'needs_context' => true,],
|
||||
'from_account_nr_contains' => ['alias' => true, 'alias_for' => 'source_account_nr_contains', 'needs_context' => true,],
|
||||
|
||||
// source account number starts with
|
||||
'source_account_nr_starts' => ['alias' => false, 'trigger_class' => SourceAccountNumberStarts::class, 'needs_context' => true,],
|
||||
'from_account_nr_starts' => ['alias' => true, 'alias_for' => 'source_account_nr_starts', 'needs_context' => true,],
|
||||
|
||||
// source account number ends with
|
||||
'source_account_nr_ends' => ['alias' => false, 'trigger_class' => SourceAccountNumberEnds::class, 'needs_context' => true,],
|
||||
'from_account_nr_ends' => ['alias' => true, 'alias_for' => 'source_account_nr_ends', 'needs_context' => true,],
|
||||
|
||||
// destination account name is + alias
|
||||
'destination_account_is' => ['alias' => false, 'trigger_class' => DestinationAccountIs::class, 'needs_context' => true,],
|
||||
'to_account_is' => ['alias' => true, 'alias_for' => 'destination_account_is', 'needs_context' => true,],
|
||||
|
||||
// destination account name contains + alias
|
||||
'destination_account_contains' => ['alias' => false, 'trigger_class' => DestinationAccountContains::class, 'needs_context' => true,],
|
||||
'to_account_contains' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,],
|
||||
'destination' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,],
|
||||
'to' => ['alias' => true, 'alias_for' => 'destination_account_contains', 'needs_context' => true,],
|
||||
|
||||
// destination account name starts with + alias
|
||||
'destination_account_starts' => ['alias' => false, 'trigger_class' => DestinationAccountStarts::class, 'needs_context' => true,],
|
||||
'to_account_starts' => ['alias' => true, 'alias_for' => 'destination_account_starts', 'needs_context' => true,],
|
||||
|
||||
// destination account name ends with + alias
|
||||
'destination_account_ends' => ['alias' => false, 'trigger_class' => DestinationAccountEnds::class, 'needs_context' => true,],
|
||||
'to_account_ends' => ['alias' => true, 'alias_for' => 'destination_account_ends', 'needs_context' => true,],
|
||||
|
||||
// destination account ID + alias
|
||||
'destination_account_id' => ['alias' => false, 'trigger_class' => DestinationAccountIdIs::class, 'needs_context' => true,],
|
||||
'to_account_id' => ['alias' => true, 'alias_for' => 'destination_account_id', 'needs_context' => true,],
|
||||
|
||||
// destination account number is
|
||||
'destination_account_nr_is' => ['alias' => false, 'trigger_class' => DestinationAccountNumberIs::class, 'needs_context' => true,],
|
||||
'to_account_nr_is' => ['alias' => true, 'alias_for' => 'destination_account_nr_is', 'needs_context' => true,],
|
||||
|
||||
// destination account number contains
|
||||
'destination_account_nr_contains' => ['alias' => false, 'trigger_class' => DestinationAccountNumberContains::class, 'needs_context' => true,],
|
||||
'to_account_nr_contains' => ['alias' => true, 'alias_for' => 'destination_account_nr_contains', 'needs_context' => true,],
|
||||
|
||||
// destination account number starts with
|
||||
'destination_account_nr_starts' => ['alias' => false, 'trigger_class' => DestinationAccountNumberStarts::class, 'needs_context' => true,],
|
||||
'to_account_nr_starts' => ['alias' => true, 'alias_for' => 'destination_account_nr_starts', 'needs_context' => true,],
|
||||
|
||||
// destination account number ends with
|
||||
'destination_account_nr_ends' => ['alias' => false, 'trigger_class' => DestinationAccountNumberEnds::class, 'needs_context' => true,],
|
||||
'to_account_nr_ends' => ['alias' => true, 'alias_for' => 'destination_account_nr_ends', 'needs_context' => true,],
|
||||
|
||||
// any account id is
|
||||
'account_id' => ['alias' => false, 'trigger_class' => AccountIdIs::class, 'needs_context' => true,], // TODO
|
||||
|
||||
// category
|
||||
'category_is' => ['alias' => false, 'trigger_class' => CategoryIs::class, 'needs_context' => true,],
|
||||
'category' => ['alias' => true, 'alias_for' => 'category_is', 'needs_context' => true,],
|
||||
'category_is' => ['alias' => false, 'trigger_class' => CategoryIs::class, 'needs_context' => true,],
|
||||
'category' => ['alias' => true, 'alias_for' => 'category_is', 'needs_context' => true,],
|
||||
|
||||
// budget
|
||||
'budget_is' => ['alias' => false, 'trigger_class' => BudgetIs::class, 'needs_context' => true,],
|
||||
'budget' => ['alias' => true, 'alias_for' => 'budget_is', 'needs_context' => true,],
|
||||
'budget_is' => ['alias' => false, 'trigger_class' => BudgetIs::class, 'needs_context' => true,],
|
||||
'budget' => ['alias' => true, 'alias_for' => 'budget_is', 'needs_context' => true,],
|
||||
|
||||
// bill
|
||||
'bill_is' => ['alias' => false, 'trigger_class' => BillIs::class, 'needs_context' => true,], // TODO
|
||||
'bill' => ['alias' => true, 'alias_for' => 'bill_is', 'needs_context' => true,],
|
||||
'bill_is' => ['alias' => false, 'trigger_class' => BillIs::class, 'needs_context' => true,], // TODO
|
||||
'bill' => ['alias' => true, 'alias_for' => 'bill_is', 'needs_context' => true,],
|
||||
|
||||
// type
|
||||
'transaction_type' => ['alias' => false, 'trigger_class' => TransactionType::class, 'needs_context' => true,],
|
||||
'type' => ['alias' => true, 'alias_for' => 'transaction_type', 'needs_context' => true,],
|
||||
'transaction_type' => ['alias' => false, 'trigger_class' => TransactionType::class, 'needs_context' => true,],
|
||||
'type' => ['alias' => true, 'alias_for' => 'transaction_type', 'needs_context' => true,],
|
||||
|
||||
// date:
|
||||
'date_is' => ['alias' => false, 'trigger_class' => DateIs::class, 'needs_context' => true,],
|
||||
'date' => ['alias' => true, 'alias_for' => 'date_is', 'needs_context' => true,],
|
||||
'on' => ['alias' => true, 'alias_for' => 'date_is', 'needs_context' => true,],
|
||||
'date_before' => ['alias' => false, 'trigger_class' => DateBefore::class, 'needs_context' => true,],
|
||||
'before' => ['alias' => true, 'alias_for' => 'date_before', 'needs_context' => true,],
|
||||
'date_after' => ['alias' => false, 'trigger_class' => DateAfter::class, 'needs_context' => true,],
|
||||
'after' => ['alias' => true, 'alias_for' => 'date_after', 'needs_context' => true,],
|
||||
'date_is' => ['alias' => false, 'trigger_class' => DateIs::class, 'needs_context' => true,],
|
||||
'date' => ['alias' => true, 'alias_for' => 'date_is', 'needs_context' => true,],
|
||||
'on' => ['alias' => true, 'alias_for' => 'date_is', 'needs_context' => true,],
|
||||
'date_before' => ['alias' => false, 'trigger_class' => DateBefore::class, 'needs_context' => true,],
|
||||
'before' => ['alias' => true, 'alias_for' => 'date_before', 'needs_context' => true,],
|
||||
'date_after' => ['alias' => false, 'trigger_class' => DateAfter::class, 'needs_context' => true,],
|
||||
'after' => ['alias' => true, 'alias_for' => 'date_after', 'needs_context' => true,],
|
||||
|
||||
// other interesting fields
|
||||
'tag_is' => ['alias' => false, 'trigger_class' => TagIs::class, 'needs_context' => true,],
|
||||
'tag' => ['alias' => true, 'alias_for' => 'tag', 'needs_context' => true,],
|
||||
'created_on' => ['alias' => false, 'trigger_class' => CreatedOn::class, 'needs_context' => true,], // TODO
|
||||
'updated_on' => ['alias' => false, 'trigger_class' => UpdatedOn::class, 'needs_context' => true,], // TODO
|
||||
'external_id' => ['alias' => false, 'trigger_class' => ExternalId::class, 'needs_context' => true,], // TODO
|
||||
'internal_reference' => ['alias' => false, 'trigger_class' => InternalReference::class, 'needs_context' => true,], // TODO
|
||||
'tag_is' => ['alias' => false, 'trigger_class' => TagIs::class, 'needs_context' => true,],
|
||||
'tag' => ['alias' => true, 'alias_for' => 'tag', 'needs_context' => true,],
|
||||
|
||||
'created_on' => ['alias' => false, 'trigger_class' => CreatedOn::class, 'needs_context' => true,],
|
||||
'created_at' => ['alias' => true, 'alias_for' => 'created_on', 'needs_context' => true,],
|
||||
|
||||
'updated_on' => ['alias' => false, 'trigger_class' => UpdatedOn::class, 'needs_context' => true,],
|
||||
'updated_at' => ['alias' => true, 'alias_for' => 'updated_on', 'needs_context' => true,],
|
||||
|
||||
'external_id' => ['alias' => false, 'trigger_class' => ExternalId::class, 'needs_context' => true,],
|
||||
'internal_reference' => ['alias' => false, 'trigger_class' => InternalReference::class, 'needs_context' => true,],
|
||||
|
||||
],
|
||||
],
|
||||
|
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false">
|
||||
|
||||
|
||||
<listeners>
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
|
||||
</listeners>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-clover" target="./storage/build/clover-all.xml" />
|
||||
</logging>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
</php>
|
||||
|
||||
|
||||
|
||||
<testsuites>
|
||||
<!--
|
||||
<testsuite name="Api">
|
||||
<directory suffix="Test.php">./tests/Api</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
-->
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./app</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
||||
|
50
phpunit.xml
50
phpunit.xml
@@ -1,6 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
|
||||
<!--
|
||||
~ phpunit.xml
|
||||
~ Copyright (c) 2020 james@firefly-iii.org
|
||||
@@ -20,20 +18,27 @@
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
stopOnFailure="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false">
|
||||
<listeners>
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
|
||||
</listeners>
|
||||
<testsuites>
|
||||
<!--
|
||||
processIsolation="false"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<listeners>
|
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener"/>
|
||||
</listeners>
|
||||
<testsuites>
|
||||
<!--
|
||||
<testsuite name="Api">
|
||||
<directory suffix="Test.php">./tests/Api</directory>
|
||||
</testsuite>
|
||||
@@ -41,19 +46,14 @@
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
-->
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./app</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
</php>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_DRIVER" value="sync"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
@@ -40,6 +40,9 @@ class BillFactoryTest extends TestCase
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
self::markTestIncomplete('Incomplete for refactor.');
|
||||
|
||||
return;
|
||||
parent::setUp();
|
||||
Log::info(sprintf('Now in %s.', get_class($this)));
|
||||
}
|
||||
|
2551
tests/Unit/Support/Search/BetterQuerySearchTest.php
Normal file
2551
tests/Unit/Support/Search/BetterQuerySearchTest.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user