mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-06 22:21:42 +00:00
Expand search with negated search options
This commit is contained in:
@@ -72,6 +72,26 @@ trait AccountCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* These accounts must not be included.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeAccounts(Collection $accounts): GroupCollectorInterface
|
||||
{
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->whereNotIn('source.account_id', $accountIds);
|
||||
$this->query->whereNotIn('destination.account_id', $accountIds);
|
||||
|
||||
app('log')->debug(sprintf('GroupCollector: excludeAccounts: %s', implode(', ', $accountIds)));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define which accounts can be part of the source and destination transactions.
|
||||
*
|
||||
@@ -95,6 +115,29 @@ trait AccountCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define which accounts can NOT be part of the source and destination transactions.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function setNotAccounts(Collection $accounts): GroupCollectorInterface
|
||||
{
|
||||
if ($accounts->count() > 0) {
|
||||
$accountIds = $accounts->pluck('id')->toArray();
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $query) use ($accountIds) {
|
||||
$query->whereNotIn('source.account_id', $accountIds);
|
||||
$query->whereNotIn('destination.account_id', $accountIds);
|
||||
}
|
||||
);
|
||||
//app('log')->debug(sprintf('GroupCollector: setAccounts: %s', implode(', ', $accountIds)));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Both source AND destination must be in this list of accounts.
|
||||
*
|
||||
|
||||
@@ -51,6 +51,20 @@ trait AmountCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function amountIsNot(string $amount): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($amount) {
|
||||
$q->where('source.amount','!=', app('steam')->negative($amount));
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transactions where the amount is less than.
|
||||
*
|
||||
@@ -106,6 +120,25 @@ trait AmountCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transactions with a specific foreign amount.
|
||||
*
|
||||
* @param string $amount
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function foreignAmountIsNot(string $amount): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($amount) {
|
||||
$q->whereNull('source.foreign_amount');
|
||||
$q->orWhere('source.foreign_amount','!=', app('steam')->negative($amount));
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transactions where the amount is less than.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -226,6 +227,53 @@ trait MetaCollection
|
||||
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesDoNotContain(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where(static function (Builder $q) use ($value) {
|
||||
$q->whereNull('notes.text');
|
||||
$q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s%%', $value));
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesDontStartWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where(static function (Builder $q) use ($value) {
|
||||
$q->whereNull('notes.text');
|
||||
$q->orWhere('notes.text', 'NOT LIKE', sprintf('%s%%', $value));
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesDontEndWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where(static function (Builder $q) use ($value) {
|
||||
$q->whereNull('notes.text');
|
||||
$q->orWhere('notes.text', 'NOT LIKE', sprintf('%%%s', $value));
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
@@ -276,6 +324,22 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesExactlyNot(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where(static function (Builder $q) use ($value) {
|
||||
$q->whereNull('notes.text');
|
||||
$q->orWhere('notes.text', '!=', sprintf('%s', $value));
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
@@ -338,6 +402,20 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function excludeBills(Collection $bills): GroupCollectorInterface
|
||||
{
|
||||
$this->withBillInformation();
|
||||
$this->query->where(static function(EloquentBuilder $q1) use ($bills) {
|
||||
$q1->whereNotIn('transaction_journals.bill_id', $bills->pluck('id')->toArray());
|
||||
$q1->orWhereNull('transaction_journals.bill_id');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific budget.
|
||||
*
|
||||
@@ -391,6 +469,22 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function excludeBudgets(Collection $budgets): GroupCollectorInterface
|
||||
{
|
||||
if ($budgets->count() > 0) {
|
||||
$this->withBudgetInformation();
|
||||
$this->query->where(static function (EloquentBuilder $q1) use ($budgets) {
|
||||
$q1->whereNotIn('budgets.id', $budgets->pluck('id')->toArray());
|
||||
$q1->orWhereNull('budgets.id');
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific bunch of categories.
|
||||
*
|
||||
@@ -409,17 +503,16 @@ trait MetaCollection
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific bunch of categories.
|
||||
*
|
||||
* @param Collection $categories
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setNotCategories(Collection $categories): GroupCollectorInterface
|
||||
public function excludeCategories(Collection $categories): GroupCollectorInterface
|
||||
{
|
||||
if ($categories->count() > 0) {
|
||||
$this->withCategoryInformation();
|
||||
$this->query->whereNotIn('categories.id', $categories->pluck('id')->toArray());
|
||||
$this->query->where(static function (EloquentBuilder $q1) use ($categories) {
|
||||
$q1->whereNotIn('categories.id', $categories->pluck('id')->toArray());
|
||||
$q1->orWhereNull('categories.id');
|
||||
});
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -461,6 +554,44 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a specific category.
|
||||
*
|
||||
* @param Category $category
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeCategory(Category $category): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
|
||||
$this->query->where(static function(EloquentBuilder $q2) use ($category) {
|
||||
$q2->where('categories.id','!=', $category->id);
|
||||
$q2->orWhereNull('categories.id');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude a specific budget.
|
||||
*
|
||||
* @param Budget $budget
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeBudget(Budget $budget): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
|
||||
$this->query->where(static function(EloquentBuilder $q2) use ($budget) {
|
||||
$q2->where('budgets.id','!=', $budget->id);
|
||||
$q2->orWhereNull('budgets.id');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
@@ -62,6 +62,16 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function dayIsNot(string $day): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereDay('transaction_journals.date', '!=', $day);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @param string $field
|
||||
@@ -142,6 +152,28 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function metaDayIsNot(string $day, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->withMetaDate($field);
|
||||
$filter = function (int $index, array $object) use ($field, $day): bool {
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon
|
||||
) {
|
||||
return (int) $day !== $transaction[$field]->day;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @param string $field
|
||||
@@ -210,6 +242,28 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function metaMonthIsNot(string $month, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->withMetaDate($field);
|
||||
$filter = function (int $index, array $object) use ($field, $month): bool {
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon
|
||||
) {
|
||||
return (int) $month !== $transaction[$field]->month;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $field
|
||||
@@ -279,6 +333,28 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function metaYearIsNot(string $year, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->withMetaDate($field);
|
||||
$filter = function (int $index, array $object) use ($field, $year): bool {
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon
|
||||
) {
|
||||
return $year !== (string) $transaction[$field]->year;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @return GroupCollectorInterface
|
||||
@@ -311,6 +387,16 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function monthIsNot(string $month): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereMonth('transaction_journals.date', '!=', $month);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @param string $field
|
||||
@@ -344,6 +430,17 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function objectDayIsNot(string $day, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereDay(sprintf('transaction_journals.%s', $field), '!=', $day);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @param string $field
|
||||
@@ -377,6 +474,17 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $month
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function objectMonthIsNot(string $month, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereMonth(sprintf('transaction_journals.%s', $field), '!=', $month);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $field
|
||||
@@ -410,6 +518,17 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function objectYearIsNot(string $year, string $field): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear(sprintf('transaction_journals.%s', $field), '!=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect transactions after a specific date.
|
||||
*
|
||||
@@ -535,6 +654,37 @@ trait TimeCollection
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeMetaDateRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface
|
||||
{
|
||||
if ($end < $start) {
|
||||
[$start, $end] = [$end, $start];
|
||||
}
|
||||
$end = clone $end; // this is so weird, but it works if $end and $start secretly point to the same object.
|
||||
$end->endOfDay();
|
||||
$start->startOfDay();
|
||||
$this->withMetaDate($field);
|
||||
|
||||
$filter = function (int $index, array $object) use ($field, $start, $end): bool {
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
if (array_key_exists($field, $transaction) && $transaction[$field] instanceof Carbon) {
|
||||
return $transaction[$field]->lt($start) || $transaction[$field]->gt($end);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
$this->postFilters[] = $filter;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $date
|
||||
* @param string $field
|
||||
@@ -576,6 +726,23 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param string $field
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeObjectRange(Carbon $start, Carbon $end, string $field): GroupCollectorInterface
|
||||
{
|
||||
$after = $start->format('Y-m-d 00:00:00');
|
||||
$before = $end->format('Y-m-d 23:59:59');
|
||||
|
||||
$this->query->where(sprintf('transaction_journals.%s', $field), '<', $after);
|
||||
$this->query->orWhere(sprintf('transaction_journals.%s', $field), '>', $before);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start and end time of the results to return.
|
||||
*
|
||||
@@ -599,6 +766,27 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function excludeRange(Carbon $start, Carbon $end): GroupCollectorInterface
|
||||
{
|
||||
if ($end < $start) {
|
||||
[$start, $end] = [$end, $start];
|
||||
}
|
||||
$startStr = $start->format('Y-m-d 00:00:00');
|
||||
$endStr = $end->format('Y-m-d 23:59:59');
|
||||
|
||||
$this->query->where('transaction_journals.date', '<', $startStr);
|
||||
$this->query->orWhere('transaction_journals.date', '>', $endStr);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Collect transactions updated on a specific date.
|
||||
*
|
||||
@@ -616,22 +804,44 @@ trait TimeCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function yearAfter(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '>=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function yearBefore(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '<=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function yearIs(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $year
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function yearIsNot(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '!=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user