Expand query search.

This commit is contained in:
James Cole
2022-03-21 06:31:38 +01:00
parent ad0dcc2cf4
commit aca008c911
8 changed files with 802 additions and 694 deletions

View File

@@ -216,9 +216,9 @@ trait AccountCollection
$this->query->leftJoin('account_types as dest_account_type', 'dest_account_type.id', '=', 'dest_account.account_type_id'); $this->query->leftJoin('account_types as dest_account_type', 'dest_account_type.id', '=', 'dest_account.account_type_id');
// and add fields: // and add fields:
$this->fields[] = 'dest_account.name as destination_account_name'; $this->fields[] = 'dest_account.name as destination_account_name';
$this->fields[] = 'dest_account.iban as destination_account_iban'; $this->fields[] = 'dest_account.iban as destination_account_iban';
$this->fields[] = 'dest_account_type.type as destination_account_type'; $this->fields[] = 'dest_account_type.type as destination_account_type';
$this->hasAccountInfo = true; $this->hasAccountInfo = true;
} }

View File

@@ -32,20 +32,20 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
*/ */
trait CollectorProperties trait CollectorProperties
{ {
private array $fields; private array $fields;
private bool $hasAccountInfo; private bool $hasAccountInfo;
private bool $hasBillInformation; private bool $hasBillInformation;
private bool $hasBudgetInformation; private bool $hasBudgetInformation;
private bool $hasCatInformation; private bool $hasCatInformation;
private bool $hasJoinedAttTables; private bool $hasJoinedAttTables;
private bool $hasJoinedMetaTables; private bool $hasJoinedMetaTables;
private bool $hasJoinedTagTables; private bool $hasJoinedTagTables;
private bool $hasNotesInformation; private bool $hasNotesInformation;
private array $integerFields; private array $integerFields;
private ?int $limit; private ?int $limit;
private ?int $page; private ?int $page;
private array $postFilters;
private HasMany $query; private HasMany $query;
private int $total; private int $total;
private ?User $user; private ?User $user;
private array $postFilters;
} }

View File

@@ -39,6 +39,50 @@ use Log;
*/ */
trait MetaCollection trait MetaCollection
{ {
/**
* @inheritDoc
*/
public function externalIdContains(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function externalIdEnds(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function externalIdStarts(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId));
return $this;
}
/** /**
* Where has no tags. * Where has no tags.
@@ -53,6 +97,82 @@ trait MetaCollection
return $this; return $this;
} }
/**
* @return GroupCollectorInterface
*/
public function withTagInformation(): GroupCollectorInterface
{
$this->fields[] = 'tags.id as tag_id';
$this->fields[] = 'tags.tag as tag_name';
$this->fields[] = 'tags.date as tag_date';
$this->fields[] = 'tags.description as tag_description';
$this->fields[] = 'tags.latitude as tag_latitude';
$this->fields[] = 'tags.longitude as tag_longitude';
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
$this->joinTagTables();
return $this;
}
/**
* Join table to get tag information.
*/
protected function joinTagTables(): void
{
if (false === $this->hasJoinedTagTables) {
// join some extra tables:
$this->hasJoinedTagTables = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
}
}
/**
* @inheritDoc
*/
public function internalReferenceContains(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'internal_reference');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function internalReferenceEnds(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'internal_reference');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function internalReferenceStarts(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'internal_reference');
$this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId));
return $this;
}
/** /**
* @param string $value * @param string $value
* *
@@ -66,6 +186,29 @@ trait MetaCollection
return $this; return $this;
} }
/**
* @inheritDoc
*/
public function withNotes(): GroupCollectorInterface
{
if (false === $this->hasNotesInformation) {
// join bill table
$this->query->leftJoin(
'notes',
static function (JoinClause $join) {
$join->on('notes.noteable_id', '=', 'transaction_journals.id');
$join->where('notes.noteable_type', '=', 'FireflyIII\Models\TransactionJournal');
$join->whereNull('notes.deleted_at');
}
);
// add fields
$this->fields[] = 'notes.text as notes';
$this->hasNotesInformation = true;
}
return $this;
}
/** /**
* @param string $value * @param string $value
* *
@@ -120,6 +263,25 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include bill name + ID, if any.
*
* @return GroupCollectorInterface
*/
public function withBillInformation(): GroupCollectorInterface
{
if (false === $this->hasBillInformation) {
// join bill table
$this->query->leftJoin('bills', 'bills.id', '=', 'transaction_journals.bill_id');
// add fields
$this->fields[] = 'bills.id as bill_id';
$this->fields[] = 'bills.name as bill_name';
$this->hasBillInformation = true;
}
return $this;
}
/** /**
* Limit the search to a specific set of bills. * Limit the search to a specific set of bills.
* *
@@ -150,6 +312,27 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include budget ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withBudgetInformation(): GroupCollectorInterface
{
if (false === $this->hasBudgetInformation) {
// join link table
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id');
// add fields
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->hasBudgetInformation = true;
}
return $this;
}
/** /**
* Limit the search to a specific set of budgets. * Limit the search to a specific set of budgets.
* *
@@ -184,6 +367,27 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include category ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withCategoryInformation(): GroupCollectorInterface
{
if (false === $this->hasCatInformation) {
// join link table
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id');
// add fields
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->hasCatInformation = true;
}
return $this;
}
/** /**
* Limit the search to a specific category. * Limit the search to a specific category.
* *
@@ -214,87 +418,6 @@ trait MetaCollection
return $this; return $this;
} }
/**
* @inheritDoc
*/
public function externalIdContains(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s%%', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function externalIdEnds(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('%%%s"', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function externalIdStarts(string $externalId): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_id');
$this->query->where('journal_meta.data', 'LIKE', sprintf('"%s%%', $externalId));
return $this;
}
/**
* @inheritDoc
*/
public function withoutExternalUrl(): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where(function (Builder $q1) {
$q1->where(function (Builder $q2) {
$q2->where('journal_meta.name', '=', 'external_url');
$q2->whereNull('journal_meta.data');
})->orWhere(function (Builder $q3) {
$q3->where('journal_meta.name', '!=', 'external_url');
});
});
return $this;
}
/**
* @inheritDoc
*/
public function withExternalUrl(): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where('journal_meta.name', '=', 'external_url');
$this->query->whereNotNull('journal_meta.data');
return $this;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -353,11 +476,11 @@ trait MetaCollection
$this->withTagInformation(); $this->withTagInformation();
// this method adds a "postFilter" to the collector. // this method adds a "postFilter" to the collector.
$list = $tags->pluck('tag')->toArray(); $list = $tags->pluck('tag')->toArray();
$filter = function (int $index, array $object) use ($list): bool { $filter = function (int $index, array $object) use ($list): bool {
foreach($object['transactions'] as $transaction) { foreach ($object['transactions'] as $transaction) {
foreach($transaction['tags'] as $tag) { foreach ($transaction['tags'] as $tag) {
if(in_array($tag['name'], $list)) { if (in_array($tag['name'], $list)) {
return false; return false;
} }
} }
@@ -394,25 +517,6 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include bill name + ID, if any.
*
* @return GroupCollectorInterface
*/
public function withBillInformation(): GroupCollectorInterface
{
if (false === $this->hasBillInformation) {
// join bill table
$this->query->leftJoin('bills', 'bills.id', '=', 'transaction_journals.bill_id');
// add fields
$this->fields[] = 'bills.id as bill_id';
$this->fields[] = 'bills.name as bill_name';
$this->hasBillInformation = true;
}
return $this;
}
/** /**
* Limit results to a transactions without a budget.. * Limit results to a transactions without a budget..
* *
@@ -426,27 +530,6 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include budget ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withBudgetInformation(): GroupCollectorInterface
{
if (false === $this->hasBudgetInformation) {
// join link table
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id');
// add fields
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->hasBudgetInformation = true;
}
return $this;
}
/** /**
* Limit results to a transactions without a category. * Limit results to a transactions without a category.
* *
@@ -460,64 +543,17 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Will include category ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withCategoryInformation(): GroupCollectorInterface
{
if (false === $this->hasCatInformation) {
// join link table
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id');
// add fields
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->hasCatInformation = true;
}
return $this;
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
public function withNotes(): GroupCollectorInterface public function withExternalUrl(): GroupCollectorInterface
{ {
if (false === $this->hasNotesInformation) { if (false === $this->hasJoinedMetaTables) {
// join bill table $this->hasJoinedMetaTables = true;
$this->query->leftJoin( $this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
'notes',
static function (JoinClause $join) {
$join->on('notes.noteable_id', '=', 'transaction_journals.id');
$join->where('notes.noteable_type', '=', 'FireflyIII\Models\TransactionJournal');
$join->whereNull('notes.deleted_at');
}
);
// add fields
$this->fields[] = 'notes.text as notes';
$this->hasNotesInformation = true;
} }
$this->query->where('journal_meta.name', '=', 'external_url');
return $this; $this->query->whereNotNull('journal_meta.data');
}
/**
* @return GroupCollectorInterface
*/
public function withTagInformation(): GroupCollectorInterface
{
$this->fields[] = 'tags.id as tag_id';
$this->fields[] = 'tags.tag as tag_name';
$this->fields[] = 'tags.date as tag_date';
$this->fields[] = 'tags.description as tag_description';
$this->fields[] = 'tags.latitude as tag_latitude';
$this->fields[] = 'tags.longitude as tag_longitude';
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
$this->joinTagTables();
return $this; return $this;
} }
@@ -560,6 +596,27 @@ trait MetaCollection
return $this; return $this;
} }
/**
* @inheritDoc
*/
public function withoutExternalUrl(): GroupCollectorInterface
{
if (false === $this->hasJoinedMetaTables) {
$this->hasJoinedMetaTables = true;
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
}
$this->query->where(function (Builder $q1) {
$q1->where(function (Builder $q2) {
$q2->where('journal_meta.name', '=', 'external_url');
$q2->whereNull('journal_meta.data');
})->orWhere(function (Builder $q3) {
$q3->where('journal_meta.name', '!=', 'external_url');
});
});
return $this;
}
/** /**
* @return GroupCollectorInterface * @return GroupCollectorInterface
*/ */
@@ -586,17 +643,4 @@ trait MetaCollection
return $this; return $this;
} }
/**
* Join table to get tag information.
*/
protected function joinTagTables(): void
{
if (false === $this->hasJoinedTagTables) {
// join some extra tables:
$this->hasJoinedTagTables = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
}
}
} }

View File

@@ -33,6 +33,45 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
trait TimeCollection trait TimeCollection
{ {
public function dayAfter(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '>=', $day);
return $this;
}
public function dayBefore(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '<=', $day);
return $this;
}
public function dayIs(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '=', $day);
return $this;
}
public function monthAfter(string $month): GroupCollectorInterface
{
$this->query->whereMonth('transaction_journals.date', '>=', $month);
return $this;
}
public function monthBefore(string $month): GroupCollectorInterface
{
$this->query->whereMonth('transaction_journals.date', '<=', $month);
return $this;
}
public function monthIs(string $month): GroupCollectorInterface
{
$this->query->whereMonth('transaction_journals.date', '=', $month);
return $this;
}
/** /**
* Collect transactions after a specific date. * Collect transactions after a specific date.
* *
@@ -120,22 +159,9 @@ trait TimeCollection
return $this; return $this;
} }
public function yearIs(string $year): GroupCollectorInterface public function yearAfter(string $year): GroupCollectorInterface
{ {
$this->query->whereYear('transaction_journals.date', '=', $year); $this->query->whereYear('transaction_journals.date', '>=', $year);
return $this;
}
public function monthIs(string $month): GroupCollectorInterface
{
$this->query->whereMonth('transaction_journals.date', '=', $month);
return $this;
}
public function dayIs(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '=', $day);
return $this; return $this;
} }
@@ -145,35 +171,9 @@ trait TimeCollection
return $this; return $this;
} }
public function monthBefore(string $month): GroupCollectorInterface public function yearIs(string $year): GroupCollectorInterface
{ {
$this->query->whereMonth('transaction_journals.date', '<=', $month); $this->query->whereYear('transaction_journals.date', '=', $year);
return $this;
}
public function dayBefore(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '<=', $day);
return $this;
}
public function yearAfter(string $year): GroupCollectorInterface
{
$this->query->whereYear('transaction_journals.date', '>=', $year);
return $this;
}
public function monthAfter(string $month): GroupCollectorInterface
{
$this->query->whereMonth('transaction_journals.date', '>=', $month);
return $this;
}
public function dayAfter(string $day): GroupCollectorInterface
{
$this->query->whereDay('transaction_journals.date', '>=', $day);
return $this; return $this;
} }
} }

View File

@@ -203,6 +203,26 @@ class GroupCollector implements GroupCollectorInterface
return $this; return $this;
} }
/**
*
*/
public function dumpQuery(): void
{
echo $this->query->select($this->fields)->toSql();
echo '<pre>';
print_r($this->query->getBindings());
echo '</pre>';
}
/**
*
*/
public function dumpQueryInLogs(): void
{
Log::debug($this->query->select($this->fields)->toSql());
Log::debug('Bindings', $this->query->getBindings());
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -263,388 +283,6 @@ class GroupCollector implements GroupCollectorInterface
return $collection; return $collection;
} }
/**
* Same as getGroups but everything is in a paginator.
*
* @return LengthAwarePaginator
*/
public function getPaginatedGroups(): LengthAwarePaginator
{
$set = $this->getGroups();
if (0 === $this->limit) {
$this->setLimit(50);
}
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
}
/**
* 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;
}
/**
* Limit results to a specific currency, either foreign or normal one.
*
* @param TransactionCurrency $currency
*
* @return GroupCollectorInterface
*/
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface
{
$this->query->where(
static function (EloquentBuilder $q) use ($currency) {
$q->where('source.transaction_currency_id', $currency->id);
$q->orWhere('source.foreign_currency_id', $currency->id);
}
);
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 set of specific transaction groups.
*
* @param array $groupIds
*
* @return GroupCollectorInterface
*/
public function setIds(array $groupIds): GroupCollectorInterface
{
$this->query->whereIn('transaction_groups.id', $groupIds);
return $this;
}
/**
* Limit the result to a set of specific journals.
*
* @param array $journalIds
*
* @return GroupCollectorInterface
*/
public function setJournalIds(array $journalIds): GroupCollectorInterface
{
if (!empty($journalIds)) {
// make all integers.
$integerIDs = array_map('intval', $journalIds);
$this->query->whereIn('transaction_journals.id', $integerIDs);
}
return $this;
}
/**
* Limit the number of returned entries.
*
* @param int $limit
*
* @return GroupCollectorInterface
*/
public function setLimit(int $limit): GroupCollectorInterface
{
$this->limit = $limit;
app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
return $this;
}
/**
* Set the page to get.
*
* @param int $page
*
* @return GroupCollectorInterface
*/
public function setPage(int $page): GroupCollectorInterface
{
$page = 0 === $page ? 1 : $page;
$this->page = $page;
app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
return $this;
}
/**
* Search for words in descriptions.
*
* @param array $array
*
* @return GroupCollectorInterface
*/
public function setSearchWords(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;
}
/**
* Limit the search to one specific transaction group.
*
* @param TransactionGroup $transactionGroup
*
* @return GroupCollectorInterface
*/
public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
{
$this->query->where('transaction_groups.id', $transactionGroup->id);
return $this;
}
/**
* Limit the included transaction types.
*
* @param array $types
*
* @return GroupCollectorInterface
*/
public function setTypes(array $types): GroupCollectorInterface
{
$this->query->whereIn('transaction_types.type', $types);
return $this;
}
/**
* Set the user object and start the query.
*
* @param User $user
*
* @return GroupCollectorInterface
*/
public function setUser(User $user): GroupCollectorInterface
{
if (null === $this->user) {
$this->user = $user;
$this->startQuery();
}
return $this;
}
/**
* Automatically include all stuff required to make API calls work.
*
* @return GroupCollectorInterface
*/
public function withAPIInformation(): GroupCollectorInterface
{
// include source + destination account name and type.
$this->withAccountInformation()
// include category ID + name (if any)
->withCategoryInformation()
// include budget ID + name (if any)
->withBudgetInformation()
// include bill ID + name (if any)
->withBillInformation();
return $this;
}
/**
* @inheritDoc
*/
public function withAttachmentInformation(): GroupCollectorInterface
{
$this->fields[] = 'attachments.id as attachment_id';
$this->fields[] = 'attachments.uploaded as attachment_uploaded';
$this->joinAttachmentTables();
return $this;
}
/**
* Join table to get attachment information.
*/
private function joinAttachmentTables(): void
{
if (false === $this->hasJoinedAttTables) {
// join some extra tables:
$this->hasJoinedAttTables = true;
$this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id')
->where(
static function (EloquentBuilder $q1) {
$q1->where('attachments.attachable_type', TransactionJournal::class);
//$q1->where('attachments.uploaded', true);
$q1->orWhereNull('attachments.attachable_type');
}
);
}
}
/**
* Build the query.
*/
private function startQuery(): void
{
//app('log')->debug('GroupCollector::startQuery');
$this->query = $this->user
//->transactionGroups()
//->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
->transactionJournals()
->leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
// join source transaction.
->leftJoin(
'transactions as source',
function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
->where('source.amount', '<', 0);
}
)
// join destination transaction
->leftJoin(
'transactions as destination',
function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
->where('destination.amount', '>', 0);
}
)
// left join transaction type.
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
}
/**
*
*/
public function dumpQuery(): void
{
echo $this->query->select($this->fields)->toSql();
echo '<pre>';
print_r($this->query->getBindings());
echo '</pre>';
}
/**
*
*/
public function dumpQueryInLogs(): void
{
Log::debug($this->query->select($this->fields)->toSql());
Log::debug('Bindings', $this->query->getBindings());
}
/**
* Convert a selected set of fields to arrays.
*
* @param array $array
*
* @return array
*/
private function convertToInteger(array $array): array
{
foreach ($this->integerFields as $field) {
$array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null;
}
return $array;
}
/**
* @param array $existingJournal
* @param TransactionJournal $newJournal
*
* @return array
*/
private function mergeAttachments(array $existingJournal, TransactionJournal $newJournal): array
{
$newArray = $newJournal->toArray();
if (array_key_exists('attachment_id', $newArray)) {
$attachmentId = (int) $newJournal['attachment_id'];
$existingJournal['attachments'][$attachmentId] = [
'id' => $attachmentId,
];
}
return $existingJournal;
}
/**
* @param array $existingJournal
* @param TransactionJournal $newJournal
*
* @return array
*/
private function mergeTags(array $existingJournal, TransactionJournal $newJournal): array
{
$newArray = $newJournal->toArray();
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
$tagId = (int) $newJournal['tag_id'];
$tagDate = null;
try {
$tagDate = Carbon::parse($newArray['tag_date']);
} catch (InvalidDateException $e) {
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
}
$existingJournal['tags'][$tagId] = [
'id' => (int) $newArray['tag_id'],
'name' => $newArray['tag_name'],
'date' => $tagDate,
'description' => $newArray['tag_description'],
];
}
return $existingJournal;
}
/** /**
* @param Collection $collection * @param Collection $collection
* *
@@ -754,6 +392,72 @@ class GroupCollector implements GroupCollectorInterface
return $result; return $result;
} }
/**
* Convert a selected set of fields to arrays.
*
* @param array $array
*
* @return array
*/
private function convertToInteger(array $array): array
{
foreach ($this->integerFields as $field) {
$array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null;
}
return $array;
}
/**
* @param array $existingJournal
* @param TransactionJournal $newJournal
*
* @return array
*/
private function mergeTags(array $existingJournal, TransactionJournal $newJournal): array
{
$newArray = $newJournal->toArray();
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
$tagId = (int) $newJournal['tag_id'];
$tagDate = null;
try {
$tagDate = Carbon::parse($newArray['tag_date']);
} catch (InvalidDateException $e) {
Log::debug(sprintf('Could not parse date: %s', $e->getMessage()));
}
$existingJournal['tags'][$tagId] = [
'id' => (int) $newArray['tag_id'],
'name' => $newArray['tag_name'],
'date' => $tagDate,
'description' => $newArray['tag_description'],
];
}
return $existingJournal;
}
/**
* @param array $existingJournal
* @param TransactionJournal $newJournal
*
* @return array
*/
private function mergeAttachments(array $existingJournal, TransactionJournal $newJournal): array
{
$newArray = $newJournal->toArray();
if (array_key_exists('attachment_id', $newArray)) {
$attachmentId = (int) $newJournal['attachment_id'];
$existingJournal['attachments'][$attachmentId] = [
'id' => $attachmentId,
];
}
return $existingJournal;
}
/** /**
* @param array $groups * @param array $groups
* *
@@ -824,4 +528,300 @@ class GroupCollector implements GroupCollectorInterface
} }
return $newCollection; return $newCollection;
} }
/**
* Same as getGroups but everything is in a paginator.
*
* @return LengthAwarePaginator
*/
public function getPaginatedGroups(): LengthAwarePaginator
{
$set = $this->getGroups();
if (0 === $this->limit) {
$this->setLimit(50);
}
return new LengthAwarePaginator($set, $this->total, $this->limit, $this->page);
}
/**
* Limit the number of returned entries.
*
* @param int $limit
*
* @return GroupCollectorInterface
*/
public function setLimit(int $limit): GroupCollectorInterface
{
$this->limit = $limit;
app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
return $this;
}
/**
* 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.
*/
private function joinAttachmentTables(): void
{
if (false === $this->hasJoinedAttTables) {
// join some extra tables:
$this->hasJoinedAttTables = true;
$this->query->leftJoin('attachments', 'attachments.attachable_id', '=', 'transaction_journals.id')
->where(
static function (EloquentBuilder $q1) {
$q1->where('attachments.attachable_type', TransactionJournal::class);
//$q1->where('attachments.uploaded', true);
$q1->orWhereNull('attachments.attachable_type');
}
);
}
}
/**
* Limit results to a specific currency, either foreign or normal one.
*
* @param TransactionCurrency $currency
*
* @return GroupCollectorInterface
*/
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface
{
$this->query->where(
static function (EloquentBuilder $q) use ($currency) {
$q->where('source.transaction_currency_id', $currency->id);
$q->orWhere('source.foreign_currency_id', $currency->id);
}
);
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 set of specific transaction groups.
*
* @param array $groupIds
*
* @return GroupCollectorInterface
*/
public function setIds(array $groupIds): GroupCollectorInterface
{
$this->query->whereIn('transaction_groups.id', $groupIds);
return $this;
}
/**
* Limit the result to a set of specific journals.
*
* @param array $journalIds
*
* @return GroupCollectorInterface
*/
public function setJournalIds(array $journalIds): GroupCollectorInterface
{
if (!empty($journalIds)) {
// make all integers.
$integerIDs = array_map('intval', $journalIds);
$this->query->whereIn('transaction_journals.id', $integerIDs);
}
return $this;
}
/**
* Set the page to get.
*
* @param int $page
*
* @return GroupCollectorInterface
*/
public function setPage(int $page): GroupCollectorInterface
{
$page = 0 === $page ? 1 : $page;
$this->page = $page;
app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
return $this;
}
/**
* Search for words in descriptions.
*
* @param array $array
*
* @return GroupCollectorInterface
*/
public function setSearchWords(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;
}
/**
* Limit the search to one specific transaction group.
*
* @param TransactionGroup $transactionGroup
*
* @return GroupCollectorInterface
*/
public function setTransactionGroup(TransactionGroup $transactionGroup): GroupCollectorInterface
{
$this->query->where('transaction_groups.id', $transactionGroup->id);
return $this;
}
/**
* Limit the included transaction types.
*
* @param array $types
*
* @return GroupCollectorInterface
*/
public function setTypes(array $types): GroupCollectorInterface
{
$this->query->whereIn('transaction_types.type', $types);
return $this;
}
/**
* Set the user object and start the query.
*
* @param User $user
*
* @return GroupCollectorInterface
*/
public function setUser(User $user): GroupCollectorInterface
{
if (null === $this->user) {
$this->user = $user;
$this->startQuery();
}
return $this;
}
/**
* Build the query.
*/
private function startQuery(): void
{
//app('log')->debug('GroupCollector::startQuery');
$this->query = $this->user
//->transactionGroups()
//->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
->transactionJournals()
->leftJoin('transaction_groups', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
// join source transaction.
->leftJoin(
'transactions as source',
function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
->where('source.amount', '<', 0);
}
)
// join destination transaction
->leftJoin(
'transactions as destination',
function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
->where('destination.amount', '>', 0);
}
)
// left join transaction type.
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
}
/**
* Automatically include all stuff required to make API calls work.
*
* @return GroupCollectorInterface
*/
public function withAPIInformation(): GroupCollectorInterface
{
// include source + destination account name and type.
$this->withAccountInformation()
// include category ID + name (if any)
->withCategoryInformation()
// include budget ID + name (if any)
->withBudgetInformation()
// include bill ID + name (if any)
->withBillInformation();
return $this;
}
/**
* @inheritDoc
*/
public function withAttachmentInformation(): GroupCollectorInterface
{
$this->fields[] = 'attachments.id as attachment_id';
$this->fields[] = 'attachments.uploaded as attachment_uploaded';
$this->joinAttachmentTables();
return $this;
}
} }

View File

@@ -66,6 +66,24 @@ interface GroupCollectorInterface
*/ */
public function amountMore(string $amount): GroupCollectorInterface; public function amountMore(string $amount): GroupCollectorInterface;
/**
* @param string $day
* @return GroupCollectorInterface
*/
public function dayAfter(string $day): GroupCollectorInterface;
/**
* @param string $day
* @return GroupCollectorInterface
*/
public function dayBefore(string $day): GroupCollectorInterface;
/**
* @param string $day
* @return GroupCollectorInterface
*/
public function dayIs(string $day): GroupCollectorInterface;
/** /**
* End of the description must match: * End of the description must match:
* *
@@ -111,6 +129,24 @@ interface GroupCollectorInterface
*/ */
public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface; public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdContains(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdEnds(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdStarts(string $externalId): GroupCollectorInterface;
/** /**
* Ensure the search will find nothing at all, zero results. * Ensure the search will find nothing at all, zero results.
* *
@@ -151,6 +187,42 @@ interface GroupCollectorInterface
*/ */
public function hasAttachments(): GroupCollectorInterface; public function hasAttachments(): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function internalReferenceContains(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function internalReferenceEnds(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function internalReferenceStarts(string $externalId): GroupCollectorInterface;
/**
* @param string $month
* @return GroupCollectorInterface
*/
public function monthAfter(string $month): GroupCollectorInterface;
/**
* @param string $month
* @return GroupCollectorInterface
*/
public function monthBefore(string $month): GroupCollectorInterface;
/**
* @param string $month
* @return GroupCollectorInterface
*/
public function monthIs(string $month): GroupCollectorInterface;
/** /**
* @param string $value * @param string $value
* *
@@ -305,38 +377,6 @@ interface GroupCollectorInterface
*/ */
public function setExternalId(string $externalId): GroupCollectorInterface; public function setExternalId(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdContains(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdStarts(string $externalId): GroupCollectorInterface;
/**
* @param string $externalId
* @return GroupCollectorInterface
*/
public function externalIdEnds(string $externalId): GroupCollectorInterface;
/**
* Transactions without an external URL
*
* @return GroupCollectorInterface
*/
public function withoutExternalUrl(): GroupCollectorInterface;
/**
* Transactions with an external URL
*
* @return GroupCollectorInterface
*/
public function withExternalUrl(): GroupCollectorInterface;
/** /**
* Limit results to a specific foreign currency. * Limit results to a specific foreign currency.
* *
@@ -437,15 +477,6 @@ interface GroupCollectorInterface
*/ */
public function setTags(Collection $tags): GroupCollectorInterface; public function setTags(Collection $tags): GroupCollectorInterface;
/**
* Only when does not have these tags
*
* @param Collection $tags
*
* @return GroupCollectorInterface
*/
public function setWithoutSpecificTags(Collection $tags): GroupCollectorInterface;
/** /**
* Limit the search to one specific transaction group. * Limit the search to one specific transaction group.
* *
@@ -482,6 +513,15 @@ interface GroupCollectorInterface
*/ */
public function setUser(User $user): GroupCollectorInterface; public function setUser(User $user): GroupCollectorInterface;
/**
* Only when does not have these tags
*
* @param Collection $tags
*
* @return GroupCollectorInterface
*/
public function setWithoutSpecificTags(Collection $tags): GroupCollectorInterface;
/** /**
* Either account can be set, but NOT both. This effectively excludes internal transfers. * Either account can be set, but NOT both. This effectively excludes internal transfers.
* *
@@ -561,6 +601,13 @@ interface GroupCollectorInterface
*/ */
public function withCategoryInformation(): GroupCollectorInterface; public function withCategoryInformation(): GroupCollectorInterface;
/**
* Transactions with an external URL
*
* @return GroupCollectorInterface
*/
public function withExternalUrl(): GroupCollectorInterface;
/** /**
* Will include notes. * Will include notes.
* *
@@ -596,6 +643,13 @@ interface GroupCollectorInterface
*/ */
public function withoutCategory(): GroupCollectorInterface; public function withoutCategory(): GroupCollectorInterface;
/**
* Transactions without an external URL
*
* @return GroupCollectorInterface
*/
public function withoutExternalUrl(): GroupCollectorInterface;
/** /**
* @return GroupCollectorInterface * @return GroupCollectorInterface
*/ */
@@ -606,15 +660,22 @@ interface GroupCollectorInterface
*/ */
public function withoutTags(): GroupCollectorInterface; public function withoutTags(): GroupCollectorInterface;
/**
public function yearIs(string $year): GroupCollectorInterface; * @param string $year
public function monthIs(string $month): GroupCollectorInterface; * @return GroupCollectorInterface
public function dayIs(string $day): GroupCollectorInterface; */
public function yearBefore(string $year): GroupCollectorInterface;
public function monthBefore(string $month): GroupCollectorInterface;
public function dayBefore(string $day): GroupCollectorInterface;
public function yearAfter(string $year): GroupCollectorInterface; public function yearAfter(string $year): GroupCollectorInterface;
public function monthAfter(string $month): GroupCollectorInterface;
public function dayAfter(string $day): GroupCollectorInterface; /**
* @param string $year
* @return GroupCollectorInterface
*/
public function yearBefore(string $year): GroupCollectorInterface;
/**
* @param string $year
* @return GroupCollectorInterface
*/
public function yearIs(string $year): GroupCollectorInterface;
} }

View File

@@ -757,9 +757,18 @@ class OperatorQuerySearch implements SearchInterface
case 'external_id_ends': case 'external_id_ends':
$this->collector->externalIdEnds($value); $this->collector->externalIdEnds($value);
break; break;
case 'internal_reference': case 'internal_reference_is':
$this->collector->setInternalReference($value); $this->collector->setInternalReference($value);
break; break;
case 'internal_reference_contains':
$this->collector->internalReferenceContains($value);
break;
case 'internal_reference_starts':
$this->collector->internalReferenceStarts($value);
break;
case 'internal_reference_ends':
$this->collector->internalReferenceEnds($value);
break;
} }
return true; return true;

View File

@@ -31,7 +31,6 @@ return [
'from_account_ends' => ['alias' => true, 'alias_for' => 'source_account_ends', 'needs_context' => true,], 'from_account_ends' => ['alias' => true, 'alias_for' => 'source_account_ends', 'needs_context' => true,],
'source_account_starts' => ['alias' => false, 'needs_context' => true,], 'source_account_starts' => ['alias' => false, 'needs_context' => true,],
'from_account_starts' => ['alias' => true, 'alias_for' => 'source_account_starts', 'needs_context' => true,], 'from_account_starts' => ['alias' => true, 'alias_for' => 'source_account_starts', 'needs_context' => true,],
'source_account_nr_is' => ['alias' => false, 'needs_context' => true,], 'source_account_nr_is' => ['alias' => false, 'needs_context' => true,],
'from_account_nr_is' => ['alias' => true, 'alias_for' => 'source_account_nr_is', 'needs_context' => true,], 'from_account_nr_is' => ['alias' => true, 'alias_for' => 'source_account_nr_is', 'needs_context' => true,],
'source_account_nr_contains' => ['alias' => false, 'needs_context' => true,], 'source_account_nr_contains' => ['alias' => false, 'needs_context' => true,],
@@ -40,7 +39,6 @@ return [
'from_account_nr_ends' => ['alias' => true, 'alias_for' => 'source_account_nr_ends', 'needs_context' => true,], 'from_account_nr_ends' => ['alias' => true, 'alias_for' => 'source_account_nr_ends', 'needs_context' => true,],
'source_account_nr_starts' => ['alias' => false, 'needs_context' => true,], 'source_account_nr_starts' => ['alias' => false, 'needs_context' => true,],
'from_account_nr_starts' => ['alias' => true, 'alias_for' => 'source_account_nr_starts', 'needs_context' => true,], 'from_account_nr_starts' => ['alias' => true, 'alias_for' => 'source_account_nr_starts', 'needs_context' => true,],
'destination_account_is' => ['alias' => false, 'needs_context' => true,], 'destination_account_is' => ['alias' => false, 'needs_context' => true,],
'to_account_is' => ['alias' => true, 'alias_for' => 'destination_account_is', 'needs_context' => true,], 'to_account_is' => ['alias' => true, 'alias_for' => 'destination_account_is', 'needs_context' => true,],
'destination_account_contains' => ['alias' => false, 'needs_context' => true,], 'destination_account_contains' => ['alias' => false, 'needs_context' => true,],
@@ -51,7 +49,6 @@ return [
'to_account_ends' => ['alias' => true, 'alias_for' => 'destination_account_ends', 'needs_context' => true,], 'to_account_ends' => ['alias' => true, 'alias_for' => 'destination_account_ends', 'needs_context' => true,],
'destination_account_starts' => ['alias' => false, 'needs_context' => true,], 'destination_account_starts' => ['alias' => false, 'needs_context' => true,],
'to_account_starts' => ['alias' => true, 'alias_for' => 'destination_account_starts', 'needs_context' => true,], 'to_account_starts' => ['alias' => true, 'alias_for' => 'destination_account_starts', 'needs_context' => true,],
'destination_account_nr_is' => ['alias' => false, 'needs_context' => true,], 'destination_account_nr_is' => ['alias' => false, 'needs_context' => true,],
'to_account_nr_is' => ['alias' => true, 'alias_for' => 'destination_account_nr_is', 'needs_context' => true,], 'to_account_nr_is' => ['alias' => true, 'alias_for' => 'destination_account_nr_is', 'needs_context' => true,],
'destination_account_nr_contains' => ['alias' => false, 'needs_context' => true,], 'destination_account_nr_contains' => ['alias' => false, 'needs_context' => true,],
@@ -60,17 +57,14 @@ return [
'to_account_nr_ends' => ['alias' => true, 'alias_for' => 'destination_account_nr_ends', 'needs_context' => true,], 'to_account_nr_ends' => ['alias' => true, 'alias_for' => 'destination_account_nr_ends', 'needs_context' => true,],
'destination_account_nr_starts' => ['alias' => false, 'needs_context' => true,], 'destination_account_nr_starts' => ['alias' => false, 'needs_context' => true,],
'to_account_nr_starts' => ['alias' => true, 'alias_for' => 'destination_account_nr_starts', 'needs_context' => true,], 'to_account_nr_starts' => ['alias' => true, 'alias_for' => 'destination_account_nr_starts', 'needs_context' => true,],
'account_is' => ['alias' => false, 'needs_context' => true,], 'account_is' => ['alias' => false, 'needs_context' => true,],
'account_contains' => ['alias' => false, 'needs_context' => true,], 'account_contains' => ['alias' => false, 'needs_context' => true,],
'account_ends' => ['alias' => false, 'needs_context' => true,], 'account_ends' => ['alias' => false, 'needs_context' => true,],
'account_starts' => ['alias' => false, 'needs_context' => true,], 'account_starts' => ['alias' => false, 'needs_context' => true,],
'account_nr_is' => ['alias' => false, 'needs_context' => true,], 'account_nr_is' => ['alias' => false, 'needs_context' => true,],
'account_nr_contains' => ['alias' => false, 'needs_context' => true,], 'account_nr_contains' => ['alias' => false, 'needs_context' => true,],
'account_nr_ends' => ['alias' => false, 'needs_context' => true,], 'account_nr_ends' => ['alias' => false, 'needs_context' => true,],
'account_nr_starts' => ['alias' => false, 'needs_context' => true,], 'account_nr_starts' => ['alias' => false, 'needs_context' => true,],
'category_is' => ['alias' => false, 'needs_context' => true,], 'category_is' => ['alias' => false, 'needs_context' => true,],
'category_contains' => ['alias' => false, 'needs_context' => true,], 'category_contains' => ['alias' => false, 'needs_context' => true,],
'category' => ['alias' => true, 'alias_for' => 'category_contains', 'needs_context' => true,], 'category' => ['alias' => true, 'alias_for' => 'category_contains', 'needs_context' => true,],
@@ -86,19 +80,19 @@ return [
'bill' => ['alias' => true, 'alias_for' => 'bill_contains', 'needs_context' => true,], 'bill' => ['alias' => true, 'alias_for' => 'bill_contains', 'needs_context' => true,],
'bill_ends' => ['alias' => false, 'needs_context' => true,], 'bill_ends' => ['alias' => false, 'needs_context' => true,],
'bill_starts' => ['alias' => false, 'needs_context' => true,], 'bill_starts' => ['alias' => false, 'needs_context' => true,],
'external_id_is' => ['alias' => false, 'needs_context' => true,], 'external_id_is' => ['alias' => false, 'needs_context' => true,],
'external_id_contains' => ['alias' => false, 'needs_context' => true,], 'external_id_contains' => ['alias' => false, 'needs_context' => true,],
'external_id' => ['alias' => true, 'alias_for' => 'external_id_contains', 'needs_context' => true,], 'external_id' => ['alias' => true, 'alias_for' => 'external_id_contains', 'needs_context' => true,],
'external_id_ends' => ['alias' => false, 'needs_context' => true,], 'external_id_ends' => ['alias' => false, 'needs_context' => true,],
'external_id_starts' => ['alias' => false, 'needs_context' => true,], 'external_id_starts' => ['alias' => false, 'needs_context' => true,],
// TODO here we are!
'internal_reference_is' => ['alias' => false, 'needs_context' => true,], 'internal_reference_is' => ['alias' => false, 'needs_context' => true,],
'internal_reference_contains' => ['alias' => false, 'needs_context' => true,], 'internal_reference_contains' => ['alias' => false, 'needs_context' => true,],
'internal_reference' => ['alias' => true, 'alias_for' => 'internal_reference_contains', 'needs_context' => true,], 'internal_reference' => ['alias' => true, 'alias_for' => 'internal_reference_contains', 'needs_context' => true,],
'internal_reference_ends' => ['alias' => false, 'needs_context' => true,], 'internal_reference_ends' => ['alias' => false, 'needs_context' => true,],
'internal_reference_starts' => ['alias' => false, 'needs_context' => true,], 'internal_reference_starts' => ['alias' => false, 'needs_context' => true,],
// TODO here we are.
'external_url_is' => ['alias' => false, 'needs_context' => true,], 'external_url_is' => ['alias' => false, 'needs_context' => true,],
'external_url_contains' => ['alias' => false, 'needs_context' => true,], 'external_url_contains' => ['alias' => false, 'needs_context' => true,],
'external_url' => ['alias' => true, 'alias_for' => 'external_url_contains', 'needs_context' => true,], 'external_url' => ['alias' => true, 'alias_for' => 'external_url_contains', 'needs_context' => true,],