From e5a08d2cf130cef13e9b4897ad02a0566ccb87b3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 28 Mar 2022 07:54:52 +0200 Subject: [PATCH] Search in attachment file names and attachments notes. --- .../Extensions/AttachmentCollection.php | 284 +++++++++++++++++ .../Collector/Extensions/MetaCollection.php | 1 - .../Collector/Extensions/TimeCollection.php | 24 +- app/Helpers/Collector/GroupCollector.php | 72 +---- .../Collector/GroupCollectorInterface.php | 48 +++ .../TransactionGroupRepository.php | 4 + app/Support/Search/OperatorQuerySearch.php | 25 ++ config/search.php | 15 + resources/lang/en_US/firefly.php | 287 ++++++++++-------- resources/views/list/attachments.twig | 4 +- 10 files changed, 555 insertions(+), 209 deletions(-) create mode 100644 app/Helpers/Collector/Extensions/AttachmentCollection.php diff --git a/app/Helpers/Collector/Extensions/AttachmentCollection.php b/app/Helpers/Collector/Extensions/AttachmentCollection.php new file mode 100644 index 0000000000..470e150886 --- /dev/null +++ b/app/Helpers/Collector/Extensions/AttachmentCollection.php @@ -0,0 +1,284 @@ +hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($name): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + $result = str_contains(strtolower($attachment['filename']), strtolower($name)) || str_contains(strtolower($attachment['title']), strtolower($name)); + if (true === $result) { + return true; + } + } + } + return false; + }; + $this->postFilters[] = $filter; + + 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'); + } + ); + } + } + + /** + * @inheritDoc + */ + public function withAttachmentInformation(): GroupCollectorInterface + { + $this->fields[] = 'attachments.id as attachment_id'; + $this->fields[] = 'attachments.filename as attachment_filename'; + $this->fields[] = 'attachments.title as attachment_title'; + $this->fields[] = 'attachments.uploaded as attachment_uploaded'; + $this->joinAttachmentTables(); + + return $this; + } + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameEnds(string $name): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($name): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + $result = str_ends_with(strtolower($attachment['filename']), strtolower($name)) || str_ends_with(strtolower($attachment['title']), strtolower($name)); + if (true === $result) { + return true; + } + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameIs(string $name): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($name): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + $result = $attachment['filename'] === $name || $attachment['title'] === $name; + if (true === $result) { + return true; + } + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameStarts(string $name): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($name): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + $result = str_starts_with(strtolower($attachment['filename']), strtolower($name)) || str_starts_with(strtolower($attachment['title']), strtolower($name)); + if (true === $result) { + return true; + } + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesAre(string $value): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($value): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + /** @var Attachment $object */ + $object = auth()->user()->attachments()->find($attachment['id']); + $notes = (string) $object->notes()?->first()?->text; + return $notes !== '' && $notes === $value; + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesContains(string $value): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($value): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + /** @var Attachment $object */ + $object = auth()->user()->attachments()->find($attachment['id']); + $notes = (string) $object->notes()?->first()?->text; + return $notes !== '' && str_contains(strtolower($notes), strtolower($value)); + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesEnds(string $value): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($value): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + /** @var Attachment $object */ + $object = auth()->user()->attachments()->find($attachment['id']); + $notes = (string) $object->notes()?->first()?->text; + return $notes !== '' && str_ends_with(strtolower($notes), strtolower($value)); + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesStarts(string $value): GroupCollectorInterface + { + $this->hasAttachments(); + $this->withAttachmentInformation(); + $filter = function (int $index, array $object) use ($value): bool { + /** @var array $transaction */ + foreach ($object['transactions'] as $transaction) { + /** @var array $attachment */ + foreach ($transaction['attachments'] as $attachment) { + /** @var Attachment $object */ + $object = auth()->user()->attachments()->find($attachment['id']); + $notes = (string) $object->notes()?->first()?->text; + return $notes !== '' && str_starts_with(strtolower($notes), strtolower($value)); + } + } + return false; + }; + $this->postFilters[] = $filter; + + return $this; + } + + /** + * Has attachments + * + * @return GroupCollectorInterface + */ + public function hasNoAttachments(): GroupCollectorInterface + { + Log::debug('Add filter on no attachments.'); + $this->joinAttachmentTables(); + $this->query->whereNull('attachments.attachable_id'); + + return $this; + } +} \ No newline at end of file diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index 9b42f59aab..dadb4067f7 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -32,7 +32,6 @@ use FireflyIII\Models\Tag; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; -use Log; /** * Trait MetaCollection diff --git a/app/Helpers/Collector/Extensions/TimeCollection.php b/app/Helpers/Collector/Extensions/TimeCollection.php index 01ee086e45..bcedb18ae9 100644 --- a/app/Helpers/Collector/Extensions/TimeCollection.php +++ b/app/Helpers/Collector/Extensions/TimeCollection.php @@ -85,6 +85,18 @@ trait TimeCollection return $this; } + /** + * @inheritDoc + */ + public function withMetaDate(string $field): GroupCollectorInterface + { + $this->joinMetaDataTables(); + $this->query->where('journal_meta.name', '=', $field); + $this->query->whereNotNull('journal_meta.data'); + + return $this; + } + /** * @param string $day * @param string $field @@ -492,18 +504,6 @@ trait TimeCollection return $this; } - /** - * @inheritDoc - */ - public function withMetaDate(string $field): GroupCollectorInterface - { - $this->joinMetaDataTables(); - $this->query->where('journal_meta.name', '=', $field); - $this->query->whereNotNull('journal_meta.data'); - - return $this; - } - /** * @param Carbon $start * @param Carbon $end diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index d7e1c2f5f9..41d43bc62e 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -29,6 +29,7 @@ use Closure; use Exception; use FireflyIII\Helpers\Collector\Extensions\AccountCollection; use FireflyIII\Helpers\Collector\Extensions\AmountCollection; +use FireflyIII\Helpers\Collector\Extensions\AttachmentCollection; use FireflyIII\Helpers\Collector\Extensions\CollectorProperties; use FireflyIII\Helpers\Collector\Extensions\MetaCollection; use FireflyIII\Helpers\Collector\Extensions\TimeCollection; @@ -49,7 +50,7 @@ use Log; */ class GroupCollector implements GroupCollectorInterface { - use CollectorProperties, AccountCollection, AmountCollection, TimeCollection, MetaCollection; + use CollectorProperties, AccountCollection, AmountCollection, TimeCollection, MetaCollection, AttachmentCollection; /** * Group collector constructor. @@ -398,12 +399,19 @@ class GroupCollector implements GroupCollectorInterface $attachmentId = (int) $augumentedJournal['attachment_id']; if (0 !== $attachmentId && $uploaded) { $result['attachments'][$attachmentId] = [ - 'id' => $attachmentId, + 'id' => $attachmentId, + 'filename' => $augumentedJournal['attachment_filename'], + 'title' => $augumentedJournal['attachment_title'], ]; } } // unset various fields: - unset($result['tag_id'], $result['meta_data'], $result['meta_name'], $result['tag_name'], $result['tag_date'], $result['tag_description'], $result['tag_latitude'], $result['tag_longitude'], $result['tag_zoom_level']); + unset($result['tag_id'], $result['meta_data'], $result['meta_name'], + $result['tag_name'], $result['tag_date'], $result['tag_description'], + $result['tag_latitude'], $result['tag_longitude'], $result['tag_zoom_level'], + $result['attachment_filename'], $result['attachment_id'] + + ); return $result; } @@ -591,52 +599,6 @@ class GroupCollector implements GroupCollectorInterface 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'); - } - ); - } - } - - /** - * Has attachments - * - * @return GroupCollectorInterface - */ - public function hasNoAttachments(): GroupCollectorInterface - { - Log::debug('Add filter on no attachments.'); - $this->joinAttachmentTables(); - $this->query->whereNull('attachments.attachable_id'); - - return $this; - } /** * Limit results to a specific currency, either foreign or normal one. @@ -858,16 +820,4 @@ class GroupCollector implements GroupCollectorInterface 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; - } } diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index cb4dc96aa3..b950f12f1f 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -66,6 +66,54 @@ interface GroupCollectorInterface */ public function amountMore(string $amount): GroupCollectorInterface; + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameContains(string $name): GroupCollectorInterface; + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameEnds(string $name): GroupCollectorInterface; + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameIs(string $name): GroupCollectorInterface; + + /** + * @param string $name + * @return GroupCollectorInterface + */ + public function attachmentNameStarts(string $name): GroupCollectorInterface; + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesAre(string $value): GroupCollectorInterface; + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesContains(string $value): GroupCollectorInterface; + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesEnds(string $value): GroupCollectorInterface; + + /** + * @param string $value + * @return GroupCollectorInterface + */ + public function attachmentNotesStarts(string $value): GroupCollectorInterface; + /** * @param string $day * @return GroupCollectorInterface diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 5a8c8037e7..f8a1985832 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -40,6 +40,7 @@ use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use FireflyIII\Services\Internal\Update\GroupUpdateService; use FireflyIII\Support\NullArrayObject; @@ -100,6 +101,8 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface */ public function getAttachments(TransactionGroup $group): array { + $repository = app(AttachmentRepositoryInterface::class); + $repository->setUser($this->user); $journals = $group->transactionJournals->pluck('id')->toArray(); $set = Attachment::whereIn('attachable_id', $journals) ->where('attachable_type', TransactionJournal::class) @@ -113,6 +116,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface $result[$journalId] = $result[$journalId] ?? []; $current = $attachment->toArray(); $current['file_exists'] = true; + $current['notes'] = $repository->getNoteText($attachment); $current['journal_title'] = $attachment->attachable->description; // @phpstan-ignore-line $result[$journalId][] = $current; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index df6a8e5a17..8e1a9d9449 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -907,6 +907,31 @@ class OperatorQuerySearch implements SearchInterface case 'internal_reference_ends': $this->collector->internalReferenceEnds($value); break; + case 'attachment_name_is': + $this->collector->attachmentNameIs($value); + break; + case 'attachment_name_contains': + $this->collector->attachmentNameContains($value); + break; + case 'attachment_name_starts': + $this->collector->attachmentNameStarts($value); + break; + case 'attachment_name_ends': + $this->collector->attachmentNameEnds($value); + break; + case 'attachment_notes_are': + $this->collector->attachmentNotesAre($value); + break; + case 'attachment_notes_contains': + $this->collector->attachmentNotesContains($value); + break; + case 'attachment_notes_starts': + $this->collector->attachmentNotesStarts($value); + break; + case 'attachment_notes_ends': + $this->collector->attachmentNotesEnds($value); + break; + } return true; diff --git a/config/search.php b/config/search.php index c8eb92c29a..7c2db4f873 100644 --- a/config/search.php +++ b/config/search.php @@ -188,5 +188,20 @@ return [ 'foreign_amount_max' => ['alias' => true, 'alias_for' => 'foreign_amount_less', 'needs_context' => true,], 'foreign_amount_more' => ['alias' => false, 'needs_context' => true,], 'foreign_amount_min' => ['alias' => true, 'alias_for' => 'foreign_amount_more', 'needs_context' => true,], + 'attachment_name_is' => ['alias' => false, 'needs_context' => true], + 'attachment' => ['alias' => true, 'alias_for' => 'attachment_name_is', 'needs_context' => true], + 'attachment_is' => ['alias' => true, 'alias_for' => 'attachment_name_is', 'needs_context' => true], + 'attachment_name' => ['alias' => true, 'alias_for' => 'attachment_name_is', 'needs_context' => true], + 'attachment_name_contains' => ['alias' => false, 'needs_context' => true], + 'attachment_name_starts' => ['alias' => false, 'needs_context' => true], + 'attachment_name_ends' => ['alias' => false, 'needs_context' => true], + 'attachment_notes' => ['alias' => true, 'alias_for' => 'attachment_notes_are', 'needs_context' => true], + 'attachment_notes_are' => ['alias' => false, 'needs_context' => true], + 'attachment_notes_contains' => ['alias' => false, 'needs_context' => true], + 'attachment_notes_contain' => ['alias' => true, 'alias_for' => 'attachment_notes_contains', 'needs_context' => true], + 'attachment_notes_starts' => ['alias' => false, 'needs_context' => true], + 'attachment_notes_start' => ['alias' => true, 'alias_for' => 'attachment_notes_starts', 'needs_context' => true], + 'attachment_notes_ends' => ['alias' => false, 'needs_context' => true], + 'attachment_notes_end' => ['alias' => true, 'alias_for' => 'attachment_notes_ends', 'needs_context' => true], ], ]; \ No newline at end of file diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 79ea98e1be..a11b2d4fbe 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -357,140 +357,147 @@ return [ // new 'search_modifier_tag_is_not' => 'No tag is ":value"', - 'search_modifier_account_is' => 'Either account is ":value"', - 'search_modifier_account_contains' => 'Either account contains ":value"', - 'search_modifier_account_ends' => 'Either account ends with ":value"', - 'search_modifier_account_starts' => 'Either account starts with ":value"', - 'search_modifier_account_nr_is' => 'Either account number / IBAN is ":value"', - 'search_modifier_account_nr_contains' => 'Either account number / IBAN contains ":value"', - 'search_modifier_account_nr_ends' => 'Either account number / IBAN ends with ":value"', - 'search_modifier_account_nr_starts' => 'Either account number / IBAN starts with ":value"', - 'search_modifier_category_contains' => 'Category contains ":value"', - 'search_modifier_category_ends' => 'Category ends with ":value"', - 'search_modifier_category_starts' => 'Category starts with ":value"', - 'search_modifier_budget_contains' => 'Budget contains ":value"', - 'search_modifier_budget_ends' => 'Budget ends with ":value"', - 'search_modifier_budget_starts' => 'Budget starts with ":value"', - 'search_modifier_bill_contains' => 'Bill contains ":value"', - 'search_modifier_bill_ends' => 'Bill ends with ":value"', - 'search_modifier_bill_starts' => 'Bill starts with ":value"', - 'search_modifier_external_id_contains' => 'External ID contains ":value"', - 'search_modifier_external_id_ends' => 'External ID ends with ":value"', - 'search_modifier_external_id_starts' => 'External ID starts with ":value"', - 'search_modifier_internal_reference_contains' => 'Internal reference contains ":value"', - 'search_modifier_internal_reference_ends' => 'Internal reference ends with ":value"', - 'search_modifier_internal_reference_starts' => 'Internal reference starts with ":value"', - 'search_modifier_external_url_is' => 'External URL is ":value"', - 'search_modifier_external_url_contains' => 'External URL contains ":value"', - 'search_modifier_external_url_ends' => 'External URL ends with ":value"', - 'search_modifier_external_url_starts' => 'External URL starts with ":value"', - 'search_modifier_has_no_attachments' => 'Transaction has no attachments', - 'search_modifier_account_is_cash' => 'Either account is a cash account.', - 'search_modifier_journal_id' => 'The journal ID is ":value"', - 'search_modifier_recurrence_id' => 'The recurring transaction ID is ":value"', - 'search_modifier_foreign_amount_is' => 'The foreign amount is ":value"', - 'search_modifier_foreign_amount_less' => 'The foreign amount is less than ":value"', - 'search_modifier_foreign_amount_more' => 'The foreign amount is more than ":value"', + 'search_modifier_account_is' => 'Either account is ":value"', + 'search_modifier_account_contains' => 'Either account contains ":value"', + 'search_modifier_account_ends' => 'Either account ends with ":value"', + 'search_modifier_account_starts' => 'Either account starts with ":value"', + 'search_modifier_account_nr_is' => 'Either account number / IBAN is ":value"', + 'search_modifier_account_nr_contains' => 'Either account number / IBAN contains ":value"', + 'search_modifier_account_nr_ends' => 'Either account number / IBAN ends with ":value"', + 'search_modifier_account_nr_starts' => 'Either account number / IBAN starts with ":value"', + 'search_modifier_category_contains' => 'Category contains ":value"', + 'search_modifier_category_ends' => 'Category ends with ":value"', + 'search_modifier_category_starts' => 'Category starts with ":value"', + 'search_modifier_budget_contains' => 'Budget contains ":value"', + 'search_modifier_budget_ends' => 'Budget ends with ":value"', + 'search_modifier_budget_starts' => 'Budget starts with ":value"', + 'search_modifier_bill_contains' => 'Bill contains ":value"', + 'search_modifier_bill_ends' => 'Bill ends with ":value"', + 'search_modifier_bill_starts' => 'Bill starts with ":value"', + 'search_modifier_external_id_contains' => 'External ID contains ":value"', + 'search_modifier_external_id_ends' => 'External ID ends with ":value"', + 'search_modifier_external_id_starts' => 'External ID starts with ":value"', + 'search_modifier_internal_reference_contains' => 'Internal reference contains ":value"', + 'search_modifier_internal_reference_ends' => 'Internal reference ends with ":value"', + 'search_modifier_internal_reference_starts' => 'Internal reference starts with ":value"', + 'search_modifier_external_url_is' => 'External URL is ":value"', + 'search_modifier_external_url_contains' => 'External URL contains ":value"', + 'search_modifier_external_url_ends' => 'External URL ends with ":value"', + 'search_modifier_external_url_starts' => 'External URL starts with ":value"', + 'search_modifier_has_no_attachments' => 'Transaction has no attachments', + 'search_modifier_account_is_cash' => 'Either account is a cash account.', + 'search_modifier_journal_id' => 'The journal ID is ":value"', + 'search_modifier_recurrence_id' => 'The recurring transaction ID is ":value"', + 'search_modifier_foreign_amount_is' => 'The foreign amount is ":value"', + 'search_modifier_foreign_amount_less' => 'The foreign amount is less than ":value"', + 'search_modifier_foreign_amount_more' => 'The foreign amount is more than ":value"', // date fields - 'search_modifier_interest_date_on' => 'Transaction interest date is ":value"', - 'search_modifier_interest_date_on_year' => 'Transaction interest date is in year ":value"', - 'search_modifier_interest_date_on_month' => 'Transaction interest date is in month ":value"', - 'search_modifier_interest_date_on_day' => 'Transaction interest date is on day of month ":value"', - 'search_modifier_interest_date_before_year' => 'Transaction interest date is before or in year ":value"', - 'search_modifier_interest_date_before_month' => 'Transaction interest date is before or in month ":value"', - 'search_modifier_interest_date_before_day' => 'Transaction interest date is before or on day of month ":value"', - 'search_modifier_interest_date_after_year' => 'Transaction interest date is after or in year ":value"', - 'search_modifier_interest_date_after_month' => 'Transaction interest date is after or in month ":value"', - 'search_modifier_interest_date_after_day' => 'Transaction interest date is after or on day of month ":value"', - 'search_modifier_book_date_on_year' => 'Transaction book date is in year ":value"', - 'search_modifier_book_date_on_month' => 'Transaction book date is in month ":value"', - 'search_modifier_book_date_on_day' => 'Transaction book date is on day of month ":value"', - 'search_modifier_book_date_before_year' => 'Transaction book date is before or in year ":value"', - 'search_modifier_book_date_before_month' => 'Transaction book date is before or in month ":value"', - 'search_modifier_book_date_before_day' => 'Transaction book date is before or on day of month ":value"', - 'search_modifier_book_date_after_year' => 'Transaction book date is after or in year ":value"', - 'search_modifier_book_date_after_month' => 'Transaction book date is after or in month ":value"', - 'search_modifier_book_date_after_day' => 'Transaction book date is after or on day of month ":value"', - 'search_modifier_process_date_on_year' => 'Transaction process date is in year ":value"', - 'search_modifier_process_date_on_month' => 'Transaction process date is in month ":value"', - 'search_modifier_process_date_on_day' => 'Transaction process date is on day of month ":value"', - 'search_modifier_process_date_before_year' => 'Transaction process date is before or in year ":value"', - 'search_modifier_process_date_before_month' => 'Transaction process date is before or in month ":value"', - 'search_modifier_process_date_before_day' => 'Transaction process date is before or on day of month ":value"', - 'search_modifier_process_date_after_year' => 'Transaction process date is after or in year ":value"', - 'search_modifier_process_date_after_month' => 'Transaction process date is after or in month ":value"', - 'search_modifier_process_date_after_day' => 'Transaction process date is after or on day of month ":value"', - 'search_modifier_due_date_on_year' => 'Transaction due date is in year ":value"', - 'search_modifier_due_date_on_month' => 'Transaction due date is in month ":value"', - 'search_modifier_due_date_on_day' => 'Transaction due date is on day of month ":value"', - 'search_modifier_due_date_before_year' => 'Transaction due date is before or in year ":value"', - 'search_modifier_due_date_before_month' => 'Transaction due date is before or in month ":value"', - 'search_modifier_due_date_before_day' => 'Transaction due date is before or on day of month ":value"', - 'search_modifier_due_date_after_year' => 'Transaction due date is after or in year ":value"', - 'search_modifier_due_date_after_month' => 'Transaction due date is after or in month ":value"', - 'search_modifier_due_date_after_day' => 'Transaction due date is after or on day of month ":value"', - 'search_modifier_payment_date_on_year' => 'Transaction payment date is in year ":value"', - 'search_modifier_payment_date_on_month' => 'Transaction payment date is in month ":value"', - 'search_modifier_payment_date_on_day' => 'Transaction payment date is on day of month ":value"', - 'search_modifier_payment_date_before_year' => 'Transaction payment date is before or in year ":value"', - 'search_modifier_payment_date_before_month' => 'Transaction payment date is before or in month ":value"', - 'search_modifier_payment_date_before_day' => 'Transaction payment date is before or on day of month ":value"', - 'search_modifier_payment_date_after_year' => 'Transaction payment date is after or in year ":value"', - 'search_modifier_payment_date_after_month' => 'Transaction payment date is after or in month ":value"', - 'search_modifier_payment_date_after_day' => 'Transaction payment date is after or on day of month ":value"', - 'search_modifier_invoice_date_on_year' => 'Transaction invoice date is in year ":value"', - 'search_modifier_invoice_date_on_month' => 'Transaction invoice date is in month ":value"', - 'search_modifier_invoice_date_on_day' => 'Transaction invoice date is on day of month ":value"', - 'search_modifier_invoice_date_before_year' => 'Transaction invoice date is before or in year ":value"', - 'search_modifier_invoice_date_before_month' => 'Transaction invoice date is before or in month ":value"', - 'search_modifier_invoice_date_before_day' => 'Transaction invoice date is before or on day of month ":value"', - 'search_modifier_invoice_date_after_year' => 'Transaction invoice date is after or in year ":value"', - 'search_modifier_invoice_date_after_month' => 'Transaction invoice date is after or in month ":value"', - 'search_modifier_invoice_date_after_day' => 'Transaction invoice date is after or on day of month ":value"', + 'search_modifier_interest_date_on' => 'Transaction interest date is ":value"', + 'search_modifier_interest_date_on_year' => 'Transaction interest date is in year ":value"', + 'search_modifier_interest_date_on_month' => 'Transaction interest date is in month ":value"', + 'search_modifier_interest_date_on_day' => 'Transaction interest date is on day of month ":value"', + 'search_modifier_interest_date_before_year' => 'Transaction interest date is before or in year ":value"', + 'search_modifier_interest_date_before_month' => 'Transaction interest date is before or in month ":value"', + 'search_modifier_interest_date_before_day' => 'Transaction interest date is before or on day of month ":value"', + 'search_modifier_interest_date_after_year' => 'Transaction interest date is after or in year ":value"', + 'search_modifier_interest_date_after_month' => 'Transaction interest date is after or in month ":value"', + 'search_modifier_interest_date_after_day' => 'Transaction interest date is after or on day of month ":value"', + 'search_modifier_book_date_on_year' => 'Transaction book date is in year ":value"', + 'search_modifier_book_date_on_month' => 'Transaction book date is in month ":value"', + 'search_modifier_book_date_on_day' => 'Transaction book date is on day of month ":value"', + 'search_modifier_book_date_before_year' => 'Transaction book date is before or in year ":value"', + 'search_modifier_book_date_before_month' => 'Transaction book date is before or in month ":value"', + 'search_modifier_book_date_before_day' => 'Transaction book date is before or on day of month ":value"', + 'search_modifier_book_date_after_year' => 'Transaction book date is after or in year ":value"', + 'search_modifier_book_date_after_month' => 'Transaction book date is after or in month ":value"', + 'search_modifier_book_date_after_day' => 'Transaction book date is after or on day of month ":value"', + 'search_modifier_process_date_on_year' => 'Transaction process date is in year ":value"', + 'search_modifier_process_date_on_month' => 'Transaction process date is in month ":value"', + 'search_modifier_process_date_on_day' => 'Transaction process date is on day of month ":value"', + 'search_modifier_process_date_before_year' => 'Transaction process date is before or in year ":value"', + 'search_modifier_process_date_before_month' => 'Transaction process date is before or in month ":value"', + 'search_modifier_process_date_before_day' => 'Transaction process date is before or on day of month ":value"', + 'search_modifier_process_date_after_year' => 'Transaction process date is after or in year ":value"', + 'search_modifier_process_date_after_month' => 'Transaction process date is after or in month ":value"', + 'search_modifier_process_date_after_day' => 'Transaction process date is after or on day of month ":value"', + 'search_modifier_due_date_on_year' => 'Transaction due date is in year ":value"', + 'search_modifier_due_date_on_month' => 'Transaction due date is in month ":value"', + 'search_modifier_due_date_on_day' => 'Transaction due date is on day of month ":value"', + 'search_modifier_due_date_before_year' => 'Transaction due date is before or in year ":value"', + 'search_modifier_due_date_before_month' => 'Transaction due date is before or in month ":value"', + 'search_modifier_due_date_before_day' => 'Transaction due date is before or on day of month ":value"', + 'search_modifier_due_date_after_year' => 'Transaction due date is after or in year ":value"', + 'search_modifier_due_date_after_month' => 'Transaction due date is after or in month ":value"', + 'search_modifier_due_date_after_day' => 'Transaction due date is after or on day of month ":value"', + 'search_modifier_payment_date_on_year' => 'Transaction payment date is in year ":value"', + 'search_modifier_payment_date_on_month' => 'Transaction payment date is in month ":value"', + 'search_modifier_payment_date_on_day' => 'Transaction payment date is on day of month ":value"', + 'search_modifier_payment_date_before_year' => 'Transaction payment date is before or in year ":value"', + 'search_modifier_payment_date_before_month' => 'Transaction payment date is before or in month ":value"', + 'search_modifier_payment_date_before_day' => 'Transaction payment date is before or on day of month ":value"', + 'search_modifier_payment_date_after_year' => 'Transaction payment date is after or in year ":value"', + 'search_modifier_payment_date_after_month' => 'Transaction payment date is after or in month ":value"', + 'search_modifier_payment_date_after_day' => 'Transaction payment date is after or on day of month ":value"', + 'search_modifier_invoice_date_on_year' => 'Transaction invoice date is in year ":value"', + 'search_modifier_invoice_date_on_month' => 'Transaction invoice date is in month ":value"', + 'search_modifier_invoice_date_on_day' => 'Transaction invoice date is on day of month ":value"', + 'search_modifier_invoice_date_before_year' => 'Transaction invoice date is before or in year ":value"', + 'search_modifier_invoice_date_before_month' => 'Transaction invoice date is before or in month ":value"', + 'search_modifier_invoice_date_before_day' => 'Transaction invoice date is before or on day of month ":value"', + 'search_modifier_invoice_date_after_year' => 'Transaction invoice date is after or in year ":value"', + 'search_modifier_invoice_date_after_month' => 'Transaction invoice date is after or in month ":value"', + 'search_modifier_invoice_date_after_day' => 'Transaction invoice date is after or on day of month ":value"', // other dates - 'search_modifier_updated_at_on_year' => 'Transaction was last updated in year ":value"', - 'search_modifier_updated_at_on_month' => 'Transaction was last updated in month ":value"', - 'search_modifier_updated_at_on_day' => 'Transaction was last updated on day of month ":value"', - 'search_modifier_updated_at_before_year' => 'Transaction was last updated in or before year ":value"', - 'search_modifier_updated_at_before_month' => 'Transaction was last updated in or before month ":value"', - 'search_modifier_updated_at_before_day' => 'Transaction was last updated on or before day of month ":value"', - 'search_modifier_updated_at_after_year' => 'Transaction was last updated in or after year ":value"', - 'search_modifier_updated_at_after_month' => 'Transaction was last updated in or after month ":value"', - 'search_modifier_updated_at_after_day' => 'Transaction was last updated on or after day of month ":value"', - 'search_modifier_created_at_on_year' => 'Transaction was created in year ":value"', - 'search_modifier_created_at_on_month' => 'Transaction was created in month ":value"', - 'search_modifier_created_at_on_day' => 'Transaction was created on day of month ":value"', - 'search_modifier_created_at_before_year' => 'Transaction was created in or before year ":value"', - 'search_modifier_created_at_before_month' => 'Transaction was created in or before month ":value"', - 'search_modifier_created_at_before_day' => 'Transaction was created on or before day of month ":value"', - 'search_modifier_created_at_after_year' => 'Transaction was created in or after year ":value"', - 'search_modifier_created_at_after_month' => 'Transaction was created in or after month ":value"', - 'search_modifier_created_at_after_day' => 'Transaction was created on or after day of month ":value"', - 'search_modifier_interest_date_before' => 'Transaction interest date is on or before ":value"', - 'search_modifier_interest_date_after' => 'Transaction interest date is on or after ":value"', - 'search_modifier_book_date_on' => 'Transaction book date is on ":value"', - 'search_modifier_book_date_before' => 'Transaction book date is on or before ":value"', - 'search_modifier_book_date_after' => 'Transaction book date is on or after ":value"', - 'search_modifier_process_date_on' => 'Transaction process date is on ":value"', - 'search_modifier_process_date_before' => 'Transaction process date is on or before ":value"', - 'search_modifier_process_date_after' => 'Transaction process date is on or after ":value"', - 'search_modifier_due_date_on' => 'Transaction due date is on ":value"', - 'search_modifier_due_date_before' => 'Transaction due date is on or before ":value"', - 'search_modifier_due_date_after' => 'Transaction due date is on or after ":value"', - 'search_modifier_payment_date_on' => 'Transaction payment date is on ":value"', - 'search_modifier_payment_date_before' => 'Transaction payment date is on or before ":value"', - 'search_modifier_payment_date_after' => 'Transaction payment date is on or after ":value"', - 'search_modifier_invoice_date_on' => 'Transaction invoice date is on ":value"', - 'search_modifier_invoice_date_before' => 'Transaction invoice date is on or before ":value"', - 'search_modifier_invoice_date_after' => 'Transaction invoice date is on or after ":value"', - 'search_modifier_created_at_on' => 'Transaction was created on ":value"', - 'search_modifier_created_at_before' => 'Transaction was created on or before ":value"', - 'search_modifier_created_at_after' => 'Transaction was created on or after ":value"', - 'search_modifier_updated_at_on' => 'Transaction was updated on ":value"', - 'search_modifier_updated_at_before' => 'Transaction was updated on or before ":value"', - 'search_modifier_updated_at_after' => 'Transaction was updated on or after ":value"', - + 'search_modifier_updated_at_on_year' => 'Transaction was last updated in year ":value"', + 'search_modifier_updated_at_on_month' => 'Transaction was last updated in month ":value"', + 'search_modifier_updated_at_on_day' => 'Transaction was last updated on day of month ":value"', + 'search_modifier_updated_at_before_year' => 'Transaction was last updated in or before year ":value"', + 'search_modifier_updated_at_before_month' => 'Transaction was last updated in or before month ":value"', + 'search_modifier_updated_at_before_day' => 'Transaction was last updated on or before day of month ":value"', + 'search_modifier_updated_at_after_year' => 'Transaction was last updated in or after year ":value"', + 'search_modifier_updated_at_after_month' => 'Transaction was last updated in or after month ":value"', + 'search_modifier_updated_at_after_day' => 'Transaction was last updated on or after day of month ":value"', + 'search_modifier_created_at_on_year' => 'Transaction was created in year ":value"', + 'search_modifier_created_at_on_month' => 'Transaction was created in month ":value"', + 'search_modifier_created_at_on_day' => 'Transaction was created on day of month ":value"', + 'search_modifier_created_at_before_year' => 'Transaction was created in or before year ":value"', + 'search_modifier_created_at_before_month' => 'Transaction was created in or before month ":value"', + 'search_modifier_created_at_before_day' => 'Transaction was created on or before day of month ":value"', + 'search_modifier_created_at_after_year' => 'Transaction was created in or after year ":value"', + 'search_modifier_created_at_after_month' => 'Transaction was created in or after month ":value"', + 'search_modifier_created_at_after_day' => 'Transaction was created on or after day of month ":value"', + 'search_modifier_interest_date_before' => 'Transaction interest date is on or before ":value"', + 'search_modifier_interest_date_after' => 'Transaction interest date is on or after ":value"', + 'search_modifier_book_date_on' => 'Transaction book date is on ":value"', + 'search_modifier_book_date_before' => 'Transaction book date is on or before ":value"', + 'search_modifier_book_date_after' => 'Transaction book date is on or after ":value"', + 'search_modifier_process_date_on' => 'Transaction process date is on ":value"', + 'search_modifier_process_date_before' => 'Transaction process date is on or before ":value"', + 'search_modifier_process_date_after' => 'Transaction process date is on or after ":value"', + 'search_modifier_due_date_on' => 'Transaction due date is on ":value"', + 'search_modifier_due_date_before' => 'Transaction due date is on or before ":value"', + 'search_modifier_due_date_after' => 'Transaction due date is on or after ":value"', + 'search_modifier_payment_date_on' => 'Transaction payment date is on ":value"', + 'search_modifier_payment_date_before' => 'Transaction payment date is on or before ":value"', + 'search_modifier_payment_date_after' => 'Transaction payment date is on or after ":value"', + 'search_modifier_invoice_date_on' => 'Transaction invoice date is on ":value"', + 'search_modifier_invoice_date_before' => 'Transaction invoice date is on or before ":value"', + 'search_modifier_invoice_date_after' => 'Transaction invoice date is on or after ":value"', + 'search_modifier_created_at_on' => 'Transaction was created on ":value"', + 'search_modifier_created_at_before' => 'Transaction was created on or before ":value"', + 'search_modifier_created_at_after' => 'Transaction was created on or after ":value"', + 'search_modifier_updated_at_on' => 'Transaction was updated on ":value"', + 'search_modifier_updated_at_before' => 'Transaction was updated on or before ":value"', + 'search_modifier_updated_at_after' => 'Transaction was updated on or after ":value"', + 'search_modifier_attachment_name_is' => 'Any attachment\'s name is ":value"', + 'search_modifier_attachment_name_contains' => 'Any attachment\'s name contains ":value"', + 'search_modifier_attachment_name_starts' => 'Any attachment\'s name starts with ":value"', + 'search_modifier_attachment_name_ends' => 'Any attachment\'s name ends with ":value"', + 'search_modifier_attachment_notes_are' => 'Any attachment\'s notes are ":value"', + 'search_modifier_attachment_notes_contains' => 'Any attachment\'s notes contain ":value"', + 'search_modifier_attachment_notes_starts' => 'Any attachment\'s notes start with ":value"', + 'search_modifier_attachment_notes_ends' => 'Any attachment\'s notes end is ":value"', 'update_rule_from_query' => 'Update rule ":rule" from search query', 'create_rule_from_query' => 'Create new rule from search query', 'rule_from_search_words' => 'The rule engine has a hard time handling ":string". The suggested rule that fits your search query may give different results. Please verify the rule triggers carefully.', @@ -826,7 +833,22 @@ return [ 'rule_trigger_foreign_amount_less' => 'Foreign amount is less than ":trigger_value"', 'rule_trigger_foreign_amount_more_choice' => 'Foreign amount is more than..', 'rule_trigger_foreign_amount_more' => 'Foreign amount is more than ":trigger_value"', - + 'rule_trigger_attachment_name_is_choice' => 'Any attachment\'s name is..', + 'rule_trigger_attachment_name_is' => 'Any attachment\'s name is ":trigger_value"', + 'rule_trigger_attachment_name_contains_choice' => 'Any attachment\'s name contains..', + 'rule_trigger_attachment_name_contains' => 'Any attachment\'s name contains ":trigger_value"', + 'rule_trigger_attachment_name_starts_choice' => 'Any attachment\'s name starts with..', + 'rule_trigger_attachment_name_starts' => 'Any attachment\'s name starts with ":trigger_value"', + 'rule_trigger_attachment_name_ends_choice' => 'Any attachment\'s name ends with..', + 'rule_trigger_attachment_name_ends' => 'Any attachment\'s name ends with ":trigger_value"', + 'rule_trigger_attachment_notes_are_choice' => 'Any attachment\'s notes are..', + 'rule_trigger_attachment_notes_are' => 'Any attachment\'s notes are ":trigger_value"', + 'rule_trigger_attachment_notes_contains_choice' => 'Any attachment\'s notes contain..', + 'rule_trigger_attachment_notes_contains' => 'Any attachment\'s notes contain ":trigger_value"', + 'rule_trigger_attachment_notes_starts_choice' => 'Any attachment\'s notes start with..', + 'rule_trigger_attachment_notes_starts' => 'Any attachment\'s notes start with ":trigger_value"', + 'rule_trigger_attachment_notes_ends_choice' => 'Any attachment\'s notes end with..', + 'rule_trigger_attachment_notes_ends' => 'Any attachment\'s notes end with ":trigger_value"', // actions 'rule_action_delete_transaction_choice' => 'DELETE transaction (!)', @@ -1668,7 +1690,6 @@ return [ 'overview' => 'Overview', 'saveOnAccount' => 'Save on account', 'unknown' => 'Unknown', - 'daily' => 'Daily', 'monthly' => 'Monthly', 'profile' => 'Profile', 'errors' => 'Errors', diff --git a/resources/views/list/attachments.twig b/resources/views/list/attachments.twig index e67e669062..91b33089b8 100644 --- a/resources/views/list/attachments.twig +++ b/resources/views/list/attachments.twig @@ -27,8 +27,8 @@ {% endif %} ({{ attachment.size|filesize }}) - {% if null != attachment.notes_text and '' != attachment.notes_text %} - {{ attachment.notes_text|default('')|markdown }} + {% if null != attachment.notes and '' != attachment.notes %} + {{ attachment.notes|default('')|markdown }} {% endif %} {% endif %} {% if not attachment.file_exists %}