diff --git a/THANKS.md b/THANKS.md
index d18178f45d..f3c31b976c 100755
--- a/THANKS.md
+++ b/THANKS.md
@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2024
+- Sobuno
- TasneemTantawy
- Antônio Franco
- yparitcher
diff --git a/app/Handlers/Observer/BillObserver.php b/app/Handlers/Observer/BillObserver.php
index 8ef64f80a0..fbf90cfa56 100644
--- a/app/Handlers/Observer/BillObserver.php
+++ b/app/Handlers/Observer/BillObserver.php
@@ -35,13 +35,13 @@ class BillObserver
{
public function created(Bill $bill): void
{
-// Log::debug('Observe "created" of a bill.');
+ // Log::debug('Observe "created" of a bill.');
$this->updateNativeAmount($bill);
}
public function deleting(Bill $bill): void
{
-// app('log')->debug('Observe "deleting" of a bill.');
+ // app('log')->debug('Observe "deleting" of a bill.');
foreach ($bill->attachments()->get() as $attachment) {
$attachment->delete();
}
@@ -50,7 +50,7 @@ class BillObserver
public function updated(Bill $bill): void
{
-// Log::debug('Observe "updated" of a bill.');
+ // Log::debug('Observe "updated" of a bill.');
$this->updateNativeAmount($bill);
}
diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php
index 690d50e65a..49c05e1341 100644
--- a/app/Helpers/Collector/GroupCollector.php
+++ b/app/Helpers/Collector/GroupCollector.php
@@ -376,6 +376,7 @@ class GroupCollector implements GroupCollectorInterface
{
if (0 === count($array)) {
Log::debug('No excluded search words provided, skipping.');
+
return $this;
}
Log::debug(sprintf('%d excluded search words provided.', count($array)));
@@ -952,6 +953,7 @@ class GroupCollector implements GroupCollectorInterface
{
if (0 === count($array)) {
Log::debug('No words in array');
+
return $this;
}
Log::debug(sprintf('%d word(s) in array', count($array)));
diff --git a/app/Http/Controllers/Rule/CreateController.php b/app/Http/Controllers/Rule/CreateController.php
index ab2e59b46c..d4def2dab4 100644
--- a/app/Http/Controllers/Rule/CreateController.php
+++ b/app/Http/Controllers/Rule/CreateController.php
@@ -89,14 +89,14 @@ class CreateController extends Controller
// build triggers from query, if present.
$query = (string) $request->get('from_query');
if ('' !== $query) {
- $search = app(SearchInterface::class);
+ $search = app(SearchInterface::class);
$search->parseQuery($query);
- $words = $search->getWords();
- $excludedWords = $search->getExcludedWords();
- $operators = $search->getOperators()->toArray();
+ $words = $search->getWords();
+ $excludedWords = $search->getExcludedWords();
+ $operators = $search->getOperators()->toArray();
if (count($words) > 0) {
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => implode('', $words)]));
- foreach($words as $word) {
+ foreach ($words as $word) {
$operators[] = [
'type' => 'description_contains',
'value' => $word,
@@ -105,14 +105,14 @@ class CreateController extends Controller
}
if (count($excludedWords) > 0) {
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => implode('', $excludedWords)]));
- foreach($excludedWords as $excludedWord) {
+ foreach ($excludedWords as $excludedWord) {
$operators[] = [
'type' => '-description_contains',
'value' => $excludedWord,
];
}
}
- $oldTriggers = $this->parseFromOperators($operators);
+ $oldTriggers = $this->parseFromOperators($operators);
}
// var_dump($oldTriggers);exit;
diff --git a/app/Http/Controllers/Rule/EditController.php b/app/Http/Controllers/Rule/EditController.php
index 4c4e04b6fc..9dbcf54ae9 100644
--- a/app/Http/Controllers/Rule/EditController.php
+++ b/app/Http/Controllers/Rule/EditController.php
@@ -85,14 +85,14 @@ class EditController extends Controller
// build triggers from query, if present.
$query = (string) $request->get('from_query');
if ('' !== $query) {
- $search = app(SearchInterface::class);
+ $search = app(SearchInterface::class);
$search->parseQuery($query);
- $words = $search->getWords();
- $excludedWords = $search->getExcludedWords();
- $operators = $search->getOperators()->toArray();
+ $words = $search->getWords();
+ $excludedWords = $search->getExcludedWords();
+ $operators = $search->getOperators()->toArray();
if (count($words) > 0) {
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => implode('', $words)]));
- foreach($words as $word) {
+ foreach ($words as $word) {
$operators[] = [
'type' => 'description_contains',
'value' => $word,
@@ -101,14 +101,14 @@ class EditController extends Controller
}
if (count($excludedWords) > 0) {
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => implode('', $excludedWords)]));
- foreach($excludedWords as $excludedWord) {
+ foreach ($excludedWords as $excludedWord) {
$operators[] = [
'type' => '-description_contains',
'value' => $excludedWord,
];
}
}
- $oldTriggers = $this->parseFromOperators($operators);
+ $oldTriggers = $this->parseFromOperators($operators);
}
// has old input?
if (null !== $request->old() && is_array($request->old()) && count($request->old()) > 0) {
diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php
index 98b5a0f850..74ed2f325f 100644
--- a/app/Providers/SearchServiceProvider.php
+++ b/app/Providers/SearchServiceProvider.php
@@ -51,8 +51,8 @@ class SearchServiceProvider extends ServiceProvider
static function (): GdbotsQueryParser|QueryParser {
$implementation = config('search.query_parser');
- return match($implementation) {
- 'new' => app(QueryParser::class),
+ return match ($implementation) {
+ 'new' => app(QueryParser::class),
default => app(GdbotsQueryParser::class),
};
}
diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php
index 8a1b2db1df..821dab72e5 100644
--- a/app/Support/Navigation.php
+++ b/app/Support/Navigation.php
@@ -505,7 +505,7 @@ class Navigation
{
$format = 'Y-m-d';
$diff = $start->diffInMonths($end, true);
- //Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
+ // Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
if ($diff >= 1.001) {
// Log::debug(sprintf('Return Y-m because %s', $diff));
$format = 'Y-m';
diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php
index 4df7866212..908a394c2a 100644
--- a/app/Support/Search/OperatorQuerySearch.php
+++ b/app/Support/Search/OperatorQuerySearch.php
@@ -44,7 +44,6 @@ use FireflyIII\Support\Search\QueryParser\Node;
use FireflyIII\Support\Search\QueryParser\FieldNode;
use FireflyIII\Support\Search\QueryParser\StringNode;
use FireflyIII\Support\Search\QueryParser\NodeGroup;
-
use FireflyIII\Support\ParseDateString;
use FireflyIII\User;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -147,6 +146,7 @@ class OperatorQuerySearch implements SearchInterface
public function parseQuery(string $query): void
{
app('log')->debug(sprintf('Now in parseQuery("%s")', $query));
+
/** @var QueryParserInterface $parser */
$parser = app(QueryParserInterface::class);
app('log')->debug(sprintf('Using %s as implementation for QueryParserInterface', get_class($parser)));
@@ -182,18 +182,22 @@ class OperatorQuerySearch implements SearchInterface
switch (true) {
case $node instanceof StringNode:
$this->handleStringNode($node, $flipProhibitedFlag);
+
break;
case $node instanceof FieldNode:
$this->handleFieldNode($node, $flipProhibitedFlag);
+
break;
case $node instanceof NodeGroup:
$this->handleNodeGroup($node, $flipProhibitedFlag);
+
break;
default:
app('log')->error(sprintf('Cannot handle node %s', get_class($node)));
+
throw new FireflyException(sprintf('Firefly III search can\'t handle "%s"-nodes', get_class($node)));
}
}
@@ -207,19 +211,17 @@ class OperatorQuerySearch implements SearchInterface
}
}
-
-
private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void
{
- $string = $node->getValue();
+ $string = $node->getValue();
$prohibited = $node->isProhibited($flipProhibitedFlag);
- if($prohibited) {
+ if ($prohibited) {
app('log')->debug(sprintf('Exclude string "%s" from search string', $string));
$this->prohibitedWords[] = $string;
}
- if(!$prohibited) {
+ if (!$prohibited) {
app('log')->debug(sprintf('Add string "%s" to search string', $string));
$this->words[] = $string;
}
@@ -230,39 +232,39 @@ class OperatorQuerySearch implements SearchInterface
*/
private function handleFieldNode(FieldNode $node, bool $flipProhibitedFlag): void
{
- $operator = strtolower($node->getOperator());
- $value = $node->getValue();
+ $operator = strtolower($node->getOperator());
+ $value = $node->getValue();
$prohibited = $node->isProhibited($flipProhibitedFlag);
- $context = config(sprintf('search.operators.%s.needs_context', $operator));
+ $context = config(sprintf('search.operators.%s.needs_context', $operator));
// is an operator that needs no context, and value is false, then prohibited = true.
if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) {
$prohibited = true;
- $value = 'true';
+ $value = 'true';
}
// if the operator is prohibited, but the value is false, do an uno reverse
if ('false' === $value && $prohibited && in_array($operator, $this->validOperators, true) && false === $context) {
$prohibited = false;
- $value = 'true';
+ $value = 'true';
}
// must be valid operator:
- $inArray = in_array($operator, $this->validOperators, true);
+ $inArray = in_array($operator, $this->validOperators, true);
if ($inArray) {
if ($this->updateCollector($operator, $value, $prohibited)) {
$this->operators->push([
- 'type' => self::getRootOperator($operator),
- 'value' => $value,
+ 'type' => self::getRootOperator($operator),
+ 'value' => $value,
'prohibited' => $prohibited,
]);
app('log')->debug(sprintf('Added operator type "%s"', $operator));
}
}
- if(!$inArray) {
+ if (!$inArray) {
app('log')->debug(sprintf('Added INVALID operator type "%s"', $operator));
$this->invalidOperators[] = [
- 'type' => $operator,
+ 'type' => $operator,
'value' => $value,
];
}
diff --git a/app/Support/Search/QueryParser/FieldNode.php b/app/Support/Search/QueryParser/FieldNode.php
index f80b3449ba..a92be2a9f4 100644
--- a/app/Support/Search/QueryParser/FieldNode.php
+++ b/app/Support/Search/QueryParser/FieldNode.php
@@ -35,8 +35,8 @@ class FieldNode extends Node
public function __construct(string $operator, string $value, bool $prohibited = false)
{
- $this->operator = $operator;
- $this->value = $value;
+ $this->operator = $operator;
+ $this->value = $value;
$this->prohibited = $prohibited;
}
diff --git a/app/Support/Search/QueryParser/GdbotsQueryParser.php b/app/Support/Search/QueryParser/GdbotsQueryParser.php
index a8dd772ddc..3685e8c281 100644
--- a/app/Support/Search/QueryParser/GdbotsQueryParser.php
+++ b/app/Support/Search/QueryParser/GdbotsQueryParser.php
@@ -41,17 +41,17 @@ class GdbotsQueryParser implements QueryParserInterface
}
/**
- * @return NodeGroup
* @throws FireflyException
*/
public function parse(string $query): NodeGroup
{
try {
$result = $this->parser->parse($query);
- $nodes = array_map(
- fn(GdbotsNode\Node $node) => $this->convertNode($node),
+ $nodes = array_map(
+ fn (GdbotsNode\Node $node) => $this->convertNode($node),
$result->getNodes()
);
+
return new NodeGroup($nodes);
} catch (\LogicException|\TypeError $e) {
fwrite(STDERR, "Setting up GdbotsQueryParserTest\n");
@@ -78,9 +78,10 @@ class GdbotsQueryParser implements QueryParserInterface
case $node instanceof GdbotsNode\Subquery:
Log::debug('Subquery');
+
return new NodeGroup(
array_map(
- fn(GdbotsNode\Node $subNode) => $this->convertNode($subNode),
+ fn (GdbotsNode\Node $subNode) => $this->convertNode($subNode),
$node->getNodes()
)
);
diff --git a/app/Support/Search/QueryParser/Node.php b/app/Support/Search/QueryParser/Node.php
index ed423bb39b..8472938653 100644
--- a/app/Support/Search/QueryParser/Node.php
+++ b/app/Support/Search/QueryParser/Node.php
@@ -47,10 +47,11 @@ abstract class Node
public function isProhibited(bool $flipFlag): bool
{
if ($flipFlag) {
- //Log::debug(sprintf('This %s is (flipped) now prohibited: %s',get_class($this), var_export(!$this->prohibited, true)));
+ // Log::debug(sprintf('This %s is (flipped) now prohibited: %s',get_class($this), var_export(!$this->prohibited, true)));
return !$this->prohibited;
}
- //Log::debug(sprintf('This %s is (not flipped) now prohibited: %s',get_class($this), var_export($this->prohibited, true)));
+
+ // Log::debug(sprintf('This %s is (not flipped) now prohibited: %s',get_class($this), var_export($this->prohibited, true)));
return $this->prohibited;
}
diff --git a/app/Support/Search/QueryParser/NodeGroup.php b/app/Support/Search/QueryParser/NodeGroup.php
index fc13c2db32..8fcd2d7588 100644
--- a/app/Support/Search/QueryParser/NodeGroup.php
+++ b/app/Support/Search/QueryParser/NodeGroup.php
@@ -37,11 +37,10 @@ class NodeGroup extends Node
/**
* @param Node[] $nodes
- * @param bool $prohibited
*/
public function __construct(array $nodes, bool $prohibited = false)
{
- $this->nodes = $nodes;
+ $this->nodes = $nodes;
$this->prohibited = $prohibited;
}
diff --git a/app/Support/Search/QueryParser/QueryParser.php b/app/Support/Search/QueryParser/QueryParser.php
index de550b7253..b5d1a65d91 100644
--- a/app/Support/Search/QueryParser/QueryParser.php
+++ b/app/Support/Search/QueryParser/QueryParser.php
@@ -52,23 +52,22 @@ class QueryParser implements QueryParserInterface
private string $query;
private int $position = 0;
- /** @return NodeGroup */
public function parse(string $query): NodeGroup
{
Log::debug(sprintf('Parsing query in QueryParser: "%s"', $query));
$this->query = $query;
$this->position = 0;
+
return $this->buildNodeGroup(false);
}
- /** @return NodeGroup */
private function buildNodeGroup(bool $isSubquery, bool $prohibited = false): NodeGroup
{
$nodes = [];
$nodeResult = $this->buildNextNode($isSubquery);
- while ($nodeResult->node !== null) {
- $nodes[] = $nodeResult->node;
+ while (null !== $nodeResult->node) {
+ $nodes[] = $nodeResult->node;
if ($nodeResult->isSubqueryEnd) {
break;
}
@@ -90,13 +89,15 @@ class QueryParser implements QueryParserInterface
// If we're in a quoted string, we treat all characters except another quote as ordinary characters
if ($inQuotes) {
- if ($char !== '"') {
+ if ('"' !== $char) {
$tokenUnderConstruction .= $char;
- $this->position++;
+ ++$this->position;
+
continue;
}
// char is "
- $this->position++;
+ ++$this->position;
+
return new NodeResult(
$this->createNode($tokenUnderConstruction, $fieldName, $prohibited),
false
@@ -105,47 +106,53 @@ class QueryParser implements QueryParserInterface
switch ($char) {
case '-':
- if ($tokenUnderConstruction === '') {
+ if ('' === $tokenUnderConstruction) {
// A minus sign at the beginning of a token indicates prohibition
Log::debug('Indicate prohibition');
$prohibited = true;
}
- if ($tokenUnderConstruction !== '') {
+ if ('' !== $tokenUnderConstruction) {
// In any other location, it's just a normal character
$tokenUnderConstruction .= $char;
}
+
break;
case '"':
- if ($tokenUnderConstruction === '') {
+ if ('' === $tokenUnderConstruction) {
// A quote sign at the beginning of a token indicates the start of a quoted string
$inQuotes = true;
}
- if ($tokenUnderConstruction !== '') {
+ if ('' !== $tokenUnderConstruction) {
// In any other location, it's just a normal character
$tokenUnderConstruction .= $char;
}
+
break;
case '(':
- if ($tokenUnderConstruction === '') {
+ if ('' === $tokenUnderConstruction) {
// A left parentheses at the beginning of a token indicates the start of a subquery
- $this->position++;
- return new NodeResult($this->buildNodeGroup(true, $prohibited),
- false
+ ++$this->position;
+
+ return new NodeResult(
+ $this->buildNodeGroup(true, $prohibited),
+ false
);
}
// In any other location, it's just a normal character
$tokenUnderConstruction .= $char;
+
break;
case ')':
// A right parentheses while in a subquery means the subquery ended,
// thus also signaling the end of any node currently being built
if ($isSubquery) {
- $this->position++;
+ ++$this->position;
+
return new NodeResult(
- $tokenUnderConstruction !== ''
+ '' !== $tokenUnderConstruction
? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited)
: null,
true
@@ -153,40 +160,44 @@ class QueryParser implements QueryParserInterface
}
// In any other location, it's just a normal character
$tokenUnderConstruction .= $char;
+
break;
case ':':
- if ($tokenUnderConstruction !== '') {
+ if ('' !== $tokenUnderConstruction) {
// If we meet a colon with a left-hand side string, we know we're in a field and are about to set up the value
$fieldName = $tokenUnderConstruction;
$tokenUnderConstruction = '';
}
- if ($tokenUnderConstruction === '') {
+ if ('' === $tokenUnderConstruction) {
// In any other location, it's just a normal character
$tokenUnderConstruction .= $char;
}
+
break;
case ' ':
// A space indicates the end of a token construction if non-empty, otherwise it's just ignored
- if ($tokenUnderConstruction !== '') {
- $this->position++;
+ if ('' !== $tokenUnderConstruction) {
+ ++$this->position;
+
return new NodeResult(
$this->createNode($tokenUnderConstruction, $fieldName, $prohibited),
false
);
}
+
break;
default:
$tokenUnderConstruction .= $char;
}
- $this->position++;
+ ++$this->position;
}
- $finalNode = $tokenUnderConstruction !== '' || $fieldName !== ''
+ $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName
? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited)
: null;
@@ -195,11 +206,13 @@ class QueryParser implements QueryParserInterface
private function createNode(string $token, string $fieldName, bool $prohibited): Node
{
- if (strlen($fieldName) > 0) {
+ if ('' !== $fieldName) {
Log::debug(sprintf('Create FieldNode %s:%s (%s)', $fieldName, $token, var_export($prohibited, true)));
+
return new FieldNode(trim($fieldName), trim($token), $prohibited);
}
Log::debug(sprintf('Create StringNode "%s" (%s)', $token, var_export($prohibited, true)));
+
return new StringNode(trim($token), $prohibited);
}
}
diff --git a/app/Support/Search/QueryParser/QueryParserInterface.php b/app/Support/Search/QueryParser/QueryParserInterface.php
index 92b85e7736..158452ff98 100644
--- a/app/Support/Search/QueryParser/QueryParserInterface.php
+++ b/app/Support/Search/QueryParser/QueryParserInterface.php
@@ -28,7 +28,6 @@ namespace FireflyIII\Support\Search\QueryParser;
interface QueryParserInterface
{
/**
- * @return NodeGroup
* @throws \LogicException
* @throws \TypeError
*/
diff --git a/app/Support/Search/QueryParser/StringNode.php b/app/Support/Search/QueryParser/StringNode.php
index 2d0ed7c8a5..37d7bf5401 100644
--- a/app/Support/Search/QueryParser/StringNode.php
+++ b/app/Support/Search/QueryParser/StringNode.php
@@ -34,7 +34,7 @@ class StringNode extends Node
public function __construct(string $value, bool $prohibited = false)
{
- $this->value = $value;
+ $this->value = $value;
$this->prohibited = $prohibited;
}
diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php
index 3c9a78e19e..bc0fa0fdb1 100644
--- a/app/Support/Search/SearchInterface.php
+++ b/app/Support/Search/SearchInterface.php
@@ -38,9 +38,11 @@ interface SearchInterface
public function getModifiers(): Collection;
public function getOperators(): Collection;
+
public function getWords(): array;
public function getWordsAsString(): string;
+
public function getExcludedWords(): array;
public function hasModifiers(): bool;
diff --git a/config/firefly.php b/config/firefly.php
index 4a67bc3b1a..adad4a4edd 100644
--- a/config/firefly.php
+++ b/config/firefly.php
@@ -81,7 +81,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
- 'version' => 'develop/2025-01-03',
+ 'version' => 'develop/2025-01-05',
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25,
diff --git a/config/search.php b/config/search.php
index 249fabd363..770e167b2f 100644
--- a/config/search.php
+++ b/config/search.php
@@ -23,7 +23,7 @@
declare(strict_types=1);
return [
- 'operators' => [
+ 'operators' => [
'user_action' => ['alias' => false, 'needs_context' => true],
'account_id' => ['alias' => false, 'needs_context' => true],
'reconciled' => ['alias' => false, 'needs_context' => false],
diff --git a/package-lock.json b/package-lock.json
index b6fdc7c929..c72a9b6654 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3133,9 +3133,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.10.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.4.tgz",
- "integrity": "sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==",
+ "version": "22.10.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz",
+ "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10067,9 +10067,9 @@
"license": "MIT"
},
"node_modules/sass": {
- "version": "1.83.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.0.tgz",
- "integrity": "sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw==",
+ "version": "1.83.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.1.tgz",
+ "integrity": "sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/resources/assets/v1/src/locales/de.json b/resources/assets/v1/src/locales/de.json
index 747f177162..c1987d1d95 100644
--- a/resources/assets/v1/src/locales/de.json
+++ b/resources/assets/v1/src/locales/de.json
@@ -36,7 +36,7 @@
"is_reconciled_fields_dropped": "Da diese Buchung abgeglichen ist, k\u00f6nnen Sie weder die Konten noch den\/die Betrag\/Betr\u00e4ge aktualisieren.",
"tags": "Schlagw\u00f6rter",
"no_budget": "(kein Budget)",
- "no_bill": "(no subscription)",
+ "no_bill": "(kein Abonnement)",
"category": "Kategorie",
"attachments": "Anh\u00e4nge",
"notes": "Notizen",
@@ -52,7 +52,7 @@
"destination_account_reconciliation": "Sie k\u00f6nnen das Zielkonto einer Kontenausgleichsbuchung nicht bearbeiten.",
"source_account_reconciliation": "Sie k\u00f6nnen das Quellkonto einer Kontenausgleichsbuchung nicht bearbeiten.",
"budget": "Budget",
- "bill": "Subscription",
+ "bill": "Abonnement",
"you_create_withdrawal": "Sie haben eine Ausgabe erstellt.",
"you_create_transfer": "Sie erstellen eine Umbuchung.",
"you_create_deposit": "Sie haben eine Einnahme erstellt.",
@@ -130,15 +130,15 @@
"response": "Antwort",
"visit_webhook_url": "Webhook-URL besuchen",
"reset_webhook_secret": "Webhook Secret zur\u00fccksetzen",
- "header_exchange_rates": "Exchange rates",
- "exchange_rates_intro": "Firefly III supports downloading and using exchange rates. Read more about this in the documentation<\/a>.",
- "exchange_rates_from_to": "Between {from} and {to} (and the other way around)",
- "exchange_rates_intro_rates": "Firefly III uses the following exchange rates. The inverse is automatically calculated when it is not provided. If no exchange rate exists for the date of the transaction, Firefly III will go back in time to find one. If none are present, the rate \"1\" will be used.",
- "header_exchange_rates_rates": "Exchange rates",
- "header_exchange_rates_table": "Table with exchange rates",
- "help_rate_form": "On this day, how many {to} will you get for one {from}?",
- "add_new_rate": "Add a new exchange rate",
- "save_new_rate": "Save new rate"
+ "header_exchange_rates": "Wechselkurse",
+ "exchange_rates_intro": "Firefly III unterst\u00fctzt das Herunterladen und Verwenden von Wechselkursen. Lesen Sie mehr dar\u00fcber in der Dokumentation<\/a>.",
+ "exchange_rates_from_to": "Zwischen {from} und {to} (und umgekehrt)",
+ "exchange_rates_intro_rates": "Firefly III verwendet die folgenden Wechselkurse. Der Kehrwert wird automatisch berechnet, wenn er nicht angegeben wurde. Wenn f\u00fcr das Datum der Transaktion kein Wechselkurs vorhanden ist, sucht Firefly III in der Vergangenheit nach einem Kurs. Wenn keine vorhanden sind, wird der Kurs \u201e1\u201c verwendet.",
+ "header_exchange_rates_rates": "Wechselkurse",
+ "header_exchange_rates_table": "Tabelle mit Wechselkursen",
+ "help_rate_form": "An diesem Tag, wie viel {to} werden Sie f\u00fcr {from} bekommen?",
+ "add_new_rate": "Neuen Wechselkurs hinzuf\u00fcgen",
+ "save_new_rate": "Neuen Kurs speichern"
},
"form": {
"url": "URL",
diff --git a/resources/assets/v1/src/locales/fi.json b/resources/assets/v1/src/locales/fi.json
index 7969979e2c..5f0a19fa4e 100644
--- a/resources/assets/v1/src/locales/fi.json
+++ b/resources/assets/v1/src/locales/fi.json
@@ -162,14 +162,14 @@
},
"list": {
"active": "Aktiivinen?",
- "trigger": "Trigger",
- "response": "Response",
- "delivery": "Delivery",
+ "trigger": "Ehto",
+ "response": "Vastaus",
+ "delivery": "Toimitus",
"url": "URL",
- "secret": "Secret"
+ "secret": "Salainen tunniste"
},
"config": {
"html_language": "fi",
- "date_time_fns": "MMMM do, yyyy @ HH:mm:ss"
+ "date_time_fns": "do MMMM yyyy @\u00a0HH:mm:ss"
}
}
\ No newline at end of file
diff --git a/resources/assets/v1/src/locales/fr.json b/resources/assets/v1/src/locales/fr.json
index 845809188f..6ae76b8cfa 100644
--- a/resources/assets/v1/src/locales/fr.json
+++ b/resources/assets/v1/src/locales/fr.json
@@ -21,7 +21,7 @@
"apply_rules_checkbox": "Appliquer les r\u00e8gles",
"fire_webhooks_checkbox": "Lancer les webhooks",
"no_budget_pointer": "Vous semblez n\u2019avoir encore aucun budget. Vous devriez en cr\u00e9er un sur la page des budgets<\/a>. Les budgets peuvent vous aider \u00e0 garder une trace des d\u00e9penses.",
- "no_bill_pointer": "You seem to have no subscription yet. You should create some on the subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
+ "no_bill_pointer": "Vous semblez n\u2019avoir encore aucun abonnement. Vous devriez en cr\u00e9er un sur la page des abonnements<\/a>. Les abonnements peuvent vous aider \u00e0 garder une trace des d\u00e9penses.",
"source_account": "Compte source",
"hidden_fields_preferences": "Vous pouvez activer plus d'options d'op\u00e9rations dans vos param\u00e8tres<\/a>.",
"destination_account": "Compte de destination",
diff --git a/resources/assets/v1/src/locales/ru.json b/resources/assets/v1/src/locales/ru.json
index 5bd6948f95..fc7c45289e 100644
--- a/resources/assets/v1/src/locales/ru.json
+++ b/resources/assets/v1/src/locales/ru.json
@@ -36,7 +36,7 @@
"is_reconciled_fields_dropped": "\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u0430 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044f \u0441\u0432\u0435\u0440\u0435\u043d\u0430, \u0432\u044b \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0447\u0435\u0442\u0430, \u043d\u0438 \u0441\u0443\u043c\u043c\u0443(\u044b).",
"tags": "\u041c\u0435\u0442\u043a\u0438",
"no_budget": "(\u0432\u043d\u0435 \u0431\u044e\u0434\u0436\u0435\u0442\u0430)",
- "no_bill": "(no subscription)",
+ "no_bill": "(\u043d\u0435\u0442 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438)",
"category": "\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f",
"attachments": "\u0412\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
"notes": "\u0417\u0430\u043c\u0435\u0442\u043a\u0438",
@@ -52,7 +52,7 @@
"destination_account_reconciliation": "\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0447\u0451\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0441\u0432\u0435\u0440\u044f\u0435\u043c\u043e\u0439 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438.",
"source_account_reconciliation": "\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0447\u0451\u0442-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u043b\u044f \u0441\u0432\u0435\u0440\u044f\u0435\u043c\u043e\u0439 \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0438.",
"budget": "\u0411\u044e\u0434\u0436\u0435\u0442",
- "bill": "Subscription",
+ "bill": "\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430",
"you_create_withdrawal": "\u0412\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 \u0440\u0430\u0441\u0445\u043e\u0434.",
"you_create_transfer": "\u0412\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434.",
"you_create_deposit": "\u0412\u044b \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0435 \u0434\u043e\u0445\u043e\u0434.",
@@ -130,15 +130,15 @@
"response": "\u041e\u0442\u0432\u0435\u0442",
"visit_webhook_url": "\u041f\u043e\u0441\u0435\u0442\u0438\u0442\u044c URL \u0432\u0435\u0431\u0445\u0443\u043a\u0430",
"reset_webhook_secret": "",
- "header_exchange_rates": "Exchange rates",
+ "header_exchange_rates": "\u041a\u0443\u0440\u0441\u044b \u0432\u0430\u043b\u044e\u0442",
"exchange_rates_intro": "Firefly III supports downloading and using exchange rates. Read more about this in the documentation<\/a>.",
- "exchange_rates_from_to": "Between {from} and {to} (and the other way around)",
+ "exchange_rates_from_to": "\u041c\u0435\u0436\u0434\u0443 {from} \u0438 {to} (\u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442)",
"exchange_rates_intro_rates": "Firefly III uses the following exchange rates. The inverse is automatically calculated when it is not provided. If no exchange rate exists for the date of the transaction, Firefly III will go back in time to find one. If none are present, the rate \"1\" will be used.",
- "header_exchange_rates_rates": "Exchange rates",
- "header_exchange_rates_table": "Table with exchange rates",
+ "header_exchange_rates_rates": "\u041a\u0443\u0440\u0441\u044b \u0432\u0430\u043b\u044e\u0442",
+ "header_exchange_rates_table": "\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u043e\u0431\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u043a\u0443\u0440\u0441\u0430\u043c\u0438",
"help_rate_form": "On this day, how many {to} will you get for one {from}?",
- "add_new_rate": "Add a new exchange rate",
- "save_new_rate": "Save new rate"
+ "add_new_rate": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u043c\u0435\u043d\u043d\u044b\u0439 \u043a\u0443\u0440\u0441",
+ "save_new_rate": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0438\u0444"
},
"form": {
"url": "\u0421\u0441\u044b\u043b\u043a\u0430",
@@ -158,7 +158,7 @@
"webhook_delivery": "\u0414\u043e\u0441\u0442\u0430\u0432\u043a\u0430",
"from_currency_to_currency": "{from} → {to}",
"to_currency_from_currency": "{to} → {from}",
- "rate": "Rate"
+ "rate": "\u041e\u0446\u0435\u043d\u0438\u0442\u044c"
},
"list": {
"active": "\u0410\u043a\u0442\u0438\u0432\u0435\u043d?",
diff --git a/resources/assets/v1/src/locales/zh-cn.json b/resources/assets/v1/src/locales/zh-cn.json
index 55f1f1559b..44820298d1 100644
--- a/resources/assets/v1/src/locales/zh-cn.json
+++ b/resources/assets/v1/src/locales/zh-cn.json
@@ -21,7 +21,7 @@
"apply_rules_checkbox": "\u5e94\u7528\u89c4\u5219",
"fire_webhooks_checkbox": "\u89e6\u53d1 webhook",
"no_budget_pointer": "\u60a8\u8fd8\u6ca1\u6709\u9884\u7b97\uff0c\u60a8\u5e94\u8be5\u5728\u9884\u7b97\u9875\u9762<\/a>\u8fdb\u884c\u521b\u5efa\u3002\u9884\u7b97\u53ef\u4ee5\u5e2e\u52a9\u60a8\u8ffd\u8e2a\u652f\u51fa\u3002",
- "no_bill_pointer": "You seem to have no subscription yet. You should create some on the subscription<\/a>-page. Subscriptions can help you keep track of expenses.",
+ "no_bill_pointer": "\u60a8\u4f3c\u4e4e\u8fd8\u6ca1\u6709\u8ba2\u9605\u3002\u60a8\u5e94\u8be5\u5728 \u8ba2\u9605<\/a>-\u9875\u9762\u4e0a\u521b\u5efa\u4e00\u4e9b\u3002\u8ba2\u9605\u53ef\u4ee5\u5e2e\u52a9\u60a8\u8ddf\u8e2a\u8d39\u7528\u3002",
"source_account": "\u6765\u6e90\u8d26\u6237",
"hidden_fields_preferences": "\u60a8\u53ef\u4ee5\u5728\u504f\u597d\u8bbe\u5b9a<\/a>\u4e2d\u542f\u7528\u66f4\u591a\u4ea4\u6613\u9009\u9879\u3002",
"destination_account": "\u76ee\u6807\u8d26\u6237",
diff --git a/tests/unit/Support/Search/QueryParser/AbstractQueryParserInterfaceParseQueryTest.php b/tests/unit/Support/Search/QueryParser/AbstractQueryParserInterfaceParseQueryTest.php
index 785fd3c2cb..5d99d11aac 100644
--- a/tests/unit/Support/Search/QueryParser/AbstractQueryParserInterfaceParseQueryTest.php
+++ b/tests/unit/Support/Search/QueryParser/AbstractQueryParserInterfaceParseQueryTest.php
@@ -15,128 +15,128 @@ abstract class AbstractQueryParserInterfaceParseQueryTest extends TestCase
{
abstract protected function createParser(): QueryParserInterface;
- public static function queryDataProvider(): array
+ public static function queryDataProvider(): iterable
{
return [
- 'empty query' => [
- 'query' => '',
- 'expected' => new NodeGroup([])
+ 'empty query' => [
+ 'query' => '',
+ 'expected' => new NodeGroup([]),
],
- 'simple word' => [
- 'query' => 'groceries',
- 'expected' => new NodeGroup([new StringNode('groceries')])
+ 'simple word' => [
+ 'query' => 'groceries',
+ 'expected' => new NodeGroup([new StringNode('groceries')]),
],
- 'prohibited word' => [
- 'query' => '-groceries',
- 'expected' => new NodeGroup([new StringNode('groceries', true)])
+ 'prohibited word' => [
+ 'query' => '-groceries',
+ 'expected' => new NodeGroup([new StringNode('groceries', true)]),
],
- 'prohibited field' => [
- 'query' => '-amount:100',
- 'expected' => new NodeGroup([new FieldNode('amount', '100', true)])
+ 'prohibited field' => [
+ 'query' => '-amount:100',
+ 'expected' => new NodeGroup([new FieldNode('amount', '100', true)]),
],
- 'quoted word' => [
- 'query' => '"test phrase"',
- 'expected' => new NodeGroup([new StringNode('test phrase')])
+ 'quoted word' => [
+ 'query' => '"test phrase"',
+ 'expected' => new NodeGroup([new StringNode('test phrase')]),
],
- 'prohibited quoted word' => [
- 'query' => '-"test phrase"',
- 'expected' => new NodeGroup([new StringNode('test phrase', true)])
+ 'prohibited quoted word' => [
+ 'query' => '-"test phrase"',
+ 'expected' => new NodeGroup([new StringNode('test phrase', true)]),
],
- 'multiple words' => [
- 'query' => 'groceries shopping market',
+ 'multiple words' => [
+ 'query' => 'groceries shopping market',
'expected' => new NodeGroup([
new StringNode('groceries'),
new StringNode('shopping'),
- new StringNode('market')
- ])
+ new StringNode('market'),
+ ]),
],
- 'field operator' => [
- 'query' => 'amount:100',
- 'expected' => new NodeGroup([new FieldNode('amount', '100')])
+ 'field operator' => [
+ 'query' => 'amount:100',
+ 'expected' => new NodeGroup([new FieldNode('amount', '100')]),
],
- 'quoted field value with single space' => [
- 'query' => 'description:"test phrase"',
- 'expected' => new NodeGroup([new FieldNode('description', 'test phrase')])
+ 'quoted field value with single space' => [
+ 'query' => 'description:"test phrase"',
+ 'expected' => new NodeGroup([new FieldNode('description', 'test phrase')]),
],
- 'multiple fields' => [
- 'query' => 'amount:100 category:food',
+ 'multiple fields' => [
+ 'query' => 'amount:100 category:food',
'expected' => new NodeGroup([
new FieldNode('amount', '100'),
- new FieldNode('category', 'food')
- ])
+ new FieldNode('category', 'food'),
+ ]),
],
- 'simple subquery' => [
- 'query' => '(amount:100 category:food)',
+ 'simple subquery' => [
+ 'query' => '(amount:100 category:food)',
'expected' => new NodeGroup([
new NodeGroup([
new FieldNode('amount', '100'),
- new FieldNode('category', 'food')
- ])
- ])
+ new FieldNode('category', 'food'),
+ ]),
+ ]),
],
- 'prohibited subquery' => [
- 'query' => '-(amount:100 category:food)',
+ 'prohibited subquery' => [
+ 'query' => '-(amount:100 category:food)',
'expected' => new NodeGroup([
new NodeGroup([
new FieldNode('amount', '100'),
- new FieldNode('category', 'food')
- ], true)
- ])
+ new FieldNode('category', 'food'),
+ ], true),
+ ]),
],
- 'nested subquery' => [
- 'query' => '(amount:100 (description:"test" category:food))',
+ 'nested subquery' => [
+ 'query' => '(amount:100 (description:"test" category:food))',
'expected' => new NodeGroup([
new NodeGroup([
new FieldNode('amount', '100'),
new NodeGroup([
new FieldNode('description', 'test'),
- new FieldNode('category', 'food')
- ])
- ])
- ])
+ new FieldNode('category', 'food'),
+ ]),
+ ]),
+ ]),
],
- 'mixed words and operators' => [
- 'query' => 'groceries amount:50 shopping',
+ 'mixed words and operators' => [
+ 'query' => 'groceries amount:50 shopping',
'expected' => new NodeGroup([
new StringNode('groceries'),
new FieldNode('amount', '50'),
- new StringNode('shopping')
- ])
+ new StringNode('shopping'),
+ ]),
],
- 'subquery after field value' => [
- 'query' => 'amount:100 (description:"market" category:food)',
+ 'subquery after field value' => [
+ 'query' => 'amount:100 (description:"market" category:food)',
'expected' => new NodeGroup([
new FieldNode('amount', '100'),
new NodeGroup([
new FieldNode('description', 'market'),
- new FieldNode('category', 'food')
- ])
- ])
+ new FieldNode('category', 'food'),
+ ]),
+ ]),
],
- 'word followed by subquery' => [
- 'query' => 'groceries (amount:100 description_contains:"test")',
+ 'word followed by subquery' => [
+ 'query' => 'groceries (amount:100 description_contains:"test")',
'expected' => new NodeGroup([
new StringNode('groceries'),
new NodeGroup([
new FieldNode('amount', '100'),
- new FieldNode('description_contains', 'test')
- ])
- ])
+ new FieldNode('description_contains', 'test'),
+ ]),
+ ]),
],
'nested subquery with prohibited field' => [
- 'query' => '(amount:100 (description_contains:"test payment" -has_attachments:true))',
+ 'query' => '(amount:100 (description_contains:"test payment" -has_attachments:true))',
'expected' => new NodeGroup([
new NodeGroup([
new FieldNode('amount', '100'),
new NodeGroup([
new FieldNode('description_contains', 'test payment'),
- new FieldNode('has_attachments', 'true', true)
- ])
- ])
- ])
+ new FieldNode('has_attachments', 'true', true),
+ ]),
+ ]),
+ ]),
],
- 'complex nested subqueries' => [
- 'query' => 'shopping (amount:50 market (-category:food word description:"test phrase" (has_notes:true)))',
+ 'complex nested subqueries' => [
+ 'query' => 'shopping (amount:50 market (-category:food word description:"test phrase" (has_notes:true)))',
'expected' => new NodeGroup([
new StringNode('shopping'),
new NodeGroup([
@@ -147,51 +147,52 @@ abstract class AbstractQueryParserInterfaceParseQueryTest extends TestCase
new StringNode('word'),
new FieldNode('description', 'test phrase'),
new NodeGroup([
- new FieldNode('has_notes', 'true')
- ])
- ])
- ])
- ])
+ new FieldNode('has_notes', 'true'),
+ ]),
+ ]),
+ ]),
+ ]),
],
- 'word with multiple spaces' => [
- 'query' => '"multiple spaces"',
- 'expected' => new NodeGroup([new StringNode('multiple spaces')])
+ 'word with multiple spaces' => [
+ 'query' => '"multiple spaces"',
+ 'expected' => new NodeGroup([new StringNode('multiple spaces')]),
],
- 'field with multiple spaces in value' => [
- 'query' => 'description:"multiple spaces here"',
- 'expected' => new NodeGroup([new FieldNode('description', 'multiple spaces here')])
+ 'field with multiple spaces in value' => [
+ 'query' => 'description:"multiple spaces here"',
+ 'expected' => new NodeGroup([new FieldNode('description', 'multiple spaces here')]),
],
- 'unmatched right parenthesis in word' => [
- 'query' => 'test)word',
- 'expected' => new NodeGroup([new StringNode('test)word')])
+ 'unmatched right parenthesis in word' => [
+ 'query' => 'test)word',
+ 'expected' => new NodeGroup([new StringNode('test)word')]),
],
- 'unmatched right parenthesis in field' => [
- 'query' => 'description:test)phrase',
- 'expected' => new NodeGroup([new FieldNode('description', 'test)phrase')])
+ 'unmatched right parenthesis in field' => [
+ 'query' => 'description:test)phrase',
+ 'expected' => new NodeGroup([new FieldNode('description', 'test)phrase')]),
],
- 'subquery followed by word' => [
- 'query' => '(amount:100 category:food) shopping',
+ 'subquery followed by word' => [
+ 'query' => '(amount:100 category:food) shopping',
'expected' => new NodeGroup([
new NodeGroup([
new FieldNode('amount', '100'),
- new FieldNode('category', 'food')
+ new FieldNode('category', 'food'),
]),
- new StringNode('shopping')
- ])
- ]
+ new StringNode('shopping'),
+ ]),
+ ],
];
}
/**
* @dataProvider queryDataProvider
- * @param string $query The query string to parse
- * @param Node $expected The expected parse result
+ *
+ * @param string $query The query string to parse
+ * @param Node $expected The expected parse result
*/
public function testQueryParsing(string $query, Node $expected): void
{
$actual = $this->createParser()->parse($query);
- $this->assertEquals($expected, $actual);
+ self::assertSame($expected, $actual);
}
}
diff --git a/tests/unit/Support/Search/QueryParser/GdbotsQueryParserParseQueryTest.php b/tests/unit/Support/Search/QueryParser/GdbotsQueryParserParseQueryTest.php
index c5880fe98c..c4e4217aa5 100644
--- a/tests/unit/Support/Search/QueryParser/GdbotsQueryParserParseQueryTest.php
+++ b/tests/unit/Support/Search/QueryParser/GdbotsQueryParserParseQueryTest.php
@@ -1,5 +1,7 @@