Compare commits

...

11 Commits

Author SHA1 Message Date
github-actions[bot]
a00e8b976c Merge pull request #11458 from firefly-iii/release-1767598313
🤖 Automatically merge the PR into the develop branch.
2026-01-05 08:32:02 +01:00
JC5
e5b3c3e6bd 🤖 Auto commit for release 'develop' on 2026-01-05 2026-01-05 08:31:53 +01:00
James Cole
4d7f63273e Fix #11449 2026-01-04 20:21:41 +01:00
James Cole
04553f6fc5 Fix #11443 2026-01-03 14:46:39 +01:00
James Cole
54676715c0 Create new request for search. 2026-01-02 16:38:46 +01:00
github-actions[bot]
23246e8f5a Merge pull request #11434 from firefly-iii/release-1767337981
🤖 Automatically merge the PR into the develop branch.
2026-01-02 08:13:10 +01:00
JC5
4ccd65b4d7 🤖 Auto commit for release 'develop' on 2026-01-02 2026-01-02 08:13:01 +01:00
James Cole
2209087b94 Previous year, fixes #11433 2026-01-02 07:59:29 +01:00
github-actions[bot]
f655dcbcf8 Merge pull request #11429 from firefly-iii/release-1767278452
🤖 Automatically merge the PR into the develop branch.
2026-01-01 15:40:59 +01:00
JC5
13bb064734 🤖 Auto commit for release 'develop' on 2026-01-01 2026-01-01 15:40:52 +01:00
James Cole
5a3edbe68f Be less strict about bills. 2026-01-01 15:37:09 +01:00
14 changed files with 160 additions and 38 deletions

View File

@@ -402,16 +402,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.92.3", "version": "v3.92.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8" "reference": "9e7488b19403423e02e8403cc1eb596baf4673b0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/9e7488b19403423e02e8403cc1eb596baf4673b0",
"reference": "2ba8f5a60f6f42fb65758cfb3768434fa2d1c7e8", "reference": "9e7488b19403423e02e8403cc1eb596baf4673b0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -443,17 +443,17 @@
}, },
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.7", "facile-it/paraunit": "^1.3.1 || ^2.7",
"infection/infection": "^0.31.0", "infection/infection": "^0.31",
"justinrainbow/json-schema": "^6.5", "justinrainbow/json-schema": "^6.6",
"keradus/cli-executor": "^2.2", "keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9", "php-coveralls/php-coveralls": "^2.9",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", "phpunit/phpunit": "^9.6.31 || ^10.5.60 || ^11.5.46",
"symfony/polyfill-php85": "^1.33", "symfony/polyfill-php85": "^1.33",
"symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", "symfony/var-dumper": "^5.4.48 || ^6.4.26 || ^7.4.0 || ^8.0",
"symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" "symfony/yaml": "^5.4.45 || ^6.4.30 || ^7.4.1 || ^8.0"
}, },
"suggest": { "suggest": {
"ext-dom": "For handling output formats in XML", "ext-dom": "For handling output formats in XML",
@@ -494,7 +494,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.3" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.92.4"
}, },
"funding": [ "funding": [
{ {
@@ -502,7 +502,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-12-18T10:45:02+00:00" "time": "2026-01-04T00:38:52+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",

View File

@@ -25,11 +25,11 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Search; namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Search\TransactionSearchRequest;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Support\Search\SearchInterface; use FireflyIII\Support\Search\SearchInterface;
use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection; use League\Fractal\Resource\Collection;
@@ -42,12 +42,12 @@ class TransactionController extends Controller
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/search/searchTransactions
*/ */
public function search(Request $request, SearchInterface $searcher): JsonResponse public function search(TransactionSearchRequest $request, SearchInterface $searcher): JsonResponse
{ {
$manager = $this->getManager(); $manager = $this->getManager();
$fullQuery = (string) $request->get('query'); $fullQuery = (string) $request->attributes->get('query');
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page'); $page = $request->attributes->get('page');
$pageSize = $this->parameters->get('limit'); $pageSize = $request->attributes->get('limit');
$searcher->parseQuery($fullQuery); $searcher->parseQuery($fullQuery);
$searcher->setPage($page); $searcher->setPage($page);
$searcher->setLimit($pageSize); $searcher->setLimit($pageSize);

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* SearchQueryRequest.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Requests\Search;
use FireflyIII\Api\V1\Requests\ApiRequest;
use Illuminate\Contracts\Validation\Validator;
class SearchQueryRequest extends ApiRequest
{
public function rules(): array
{
return [
'query' => sprintf('min:0|max:500|%s', $this->required),
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
if ($validator->failed()) {
return;
}
$query = $this->convertString('query');
$this->attributes->set('query', $query);
}
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* SearchRequest.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace FireflyIII\Api\V1\Requests\Search;
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
use FireflyIII\Api\V1\Requests\PaginationRequest;
use FireflyIII\Models\TransactionJournal;
use Override;
class TransactionSearchRequest extends AggregateFormRequest
{
#[Override]
protected function getRequests(): array
{
return [
[PaginationRequest::class, 'sort_class' => TransactionJournal::class],
SearchQueryRequest::class,
// [ObjectTypeApiRequest::class, 'object_type' => Account::class],
];
}
}

View File

@@ -24,15 +24,20 @@ declare(strict_types=1);
namespace FireflyIII\Events\Model\TransactionGroup; namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event; use FireflyIII\Events\Event;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class TriggeredStoredTransactionGroup extends Event class TriggeredStoredTransactionGroup extends Event
{ {
use SerializesModels; use SerializesModels;
public ?RuleGroup $ruleGroup = null;
/** /**
* Create a new event instance. * Create a new event instance.
*/ */
public function __construct(public TransactionGroup $transactionGroup) {} public function __construct(public TransactionGroup $transactionGroup, ?RuleGroup $ruleGroup = null)
{
$this->ruleGroup = $ruleGroup;
}
} }

View File

@@ -28,6 +28,7 @@ use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Events\StoredTransactionGroup;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
@@ -46,7 +47,7 @@ class StoredGroupEventHandler
{ {
public function runAllHandlers(StoredTransactionGroup $event): void public function runAllHandlers(StoredTransactionGroup $event): void
{ {
$this->processRules($event); $this->processRules($event, null);
$this->recalculateCredit($event); $this->recalculateCredit($event);
$this->triggerWebhooks($event); $this->triggerWebhooks($event);
$this->removePeriodStatistics($event); $this->removePeriodStatistics($event);
@@ -55,13 +56,13 @@ class StoredGroupEventHandler
public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void
{ {
$newEvent = new StoredTransactionGroup($event->transactionGroup, true, false); $newEvent = new StoredTransactionGroup($event->transactionGroup, true, false);
$this->processRules($newEvent); $this->processRules($newEvent, $event->ruleGroup);
} }
/** /**
* This method grabs all the users rules and processes them. * This method grabs all the users rules and processes them.
*/ */
private function processRules(StoredTransactionGroup $storedGroupEvent): void private function processRules(StoredTransactionGroup $storedGroupEvent, ?RuleGroup $ruleGroup): void
{ {
if (false === $storedGroupEvent->applyRules) { if (false === $storedGroupEvent->applyRules) {
Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id)); Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
@@ -86,7 +87,14 @@ class StoredGroupEventHandler
// add the groups to the rule engine. // add the groups to the rule engine.
// it should run the rules in the group and cancel the group if necessary. // it should run the rules in the group and cancel the group if necessary.
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); if (null === $ruleGroup) {
Log::debug('Fire processRules with ALL store-journal rule groups.');
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
}
if (null !== $ruleGroup) {
Log::debug(sprintf('Fire processRules with rule group #%d.', $ruleGroup->id));
$groups = new Collection([$ruleGroup]);
}
// create and fire rule engine. // create and fire rule engine.
$newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine = app(RuleEngineInterface::class);

View File

@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\View\View; use Illuminate\View\View;
@@ -70,13 +71,20 @@ class ExecutionController extends Controller
*/ */
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
{ {
Log::debug(sprintf('You have selected rule group #%d', $ruleGroup->id));
// Get parameters specified by the user // Get parameters specified by the user
$accounts = $request->get('accounts'); $accounts = $request->get('accounts');
$set = $this->repository->getAccountsById($accounts); $set = new Collection();
if (is_array($accounts)) {
$set = $this->repository->getAccountsById($accounts);
}
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($set); if (count($set) > 0) {
$collector->setAccounts($set);
}
// add date operators. // add date operators.
if (null !== $request->get('start')) { if (null !== $request->get('start')) {
$startDate = new Carbon($request->get('start')); $startDate = new Carbon($request->get('start'));
@@ -96,7 +104,7 @@ class ExecutionController extends Controller
/** @var TransactionGroup $group */ /** @var TransactionGroup $group */
foreach ($groups as $group) { foreach ($groups as $group) {
Log::debug(sprintf('Processing group #%d.', $group->id)); Log::debug(sprintf('Processing group #%d.', $group->id));
event(new TriggeredStoredTransactionGroup($group)); event(new TriggeredStoredTransactionGroup($group, $ruleGroup));
} }
} }

View File

@@ -191,7 +191,7 @@ class WarnAboutBills implements ShouldQueue
$diff = $earliest->diffInDays($this->date); $diff = $earliest->diffInDays($this->date);
Log::debug(sprintf('Difference in days is %s', $diff)); Log::debug(sprintf('Difference in days is %s', $diff));
return $diff >= 2; return $diff >= 6; // FIXME hard coded value.
} }
private function sendOverdueAlerts(User $user, array $overdue): void private function sendOverdueAlerts(User $user, array $overdue): void

View File

@@ -157,6 +157,11 @@ trait GetConfigurationData
$index = (string)trans('firefly.year_to_date'); $index = (string)trans('firefly.year_to_date');
$ranges[$index] = [$yearBegin, new Carbon()]; $ranges[$index] = [$yearBegin, new Carbon()];
// previous year:
$yearBegin = today(config('app.timezone'))->subYear()->startOfYear();
$index = (string)trans('firefly.previous_year', ['year' => $yearBegin->year]);
$ranges[$index] = [$yearBegin, $yearBegin->clone()->endOfYear()];
// everything // everything
$index = (string)trans('firefly.everything'); $index = (string)trans('firefly.everything');
$ranges[$index] = [$first, new Carbon()]; $ranges[$index] = [$first, new Carbon()];

View File

@@ -321,7 +321,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
$array['foreign_currency_code'] = $entry->foreign_currency_code; $array['foreign_currency_code'] = $entry->foreign_currency_code;
$array['foreign_currency_symbol'] = $entry->foreign_currency_symbol; $array['foreign_currency_symbol'] = $entry->foreign_currency_symbol;
$array['foreign_currency_decimal_places'] = $entry->foreign_currency_decimal_places; $array['foreign_currency_decimal_places'] = $entry->foreign_currency_decimal_places;
$array['foreign_amount'] = Steam::bcround($entry->foreign_amount, $entry->foreign_currency_decimal_places); $array['foreign_amount'] = Steam::bcround((string) $entry->foreign_amount, $entry->foreign_currency_decimal_places);
} }
// convert to primary, but is already primary. // convert to primary, but is already primary.
if ($this->convertToPrimary && (int)$entry->transaction_currency_id === $this->primaryCurrency->id) { if ($this->convertToPrimary && (int)$entry->transaction_currency_id === $this->primaryCurrency->id) {
@@ -329,7 +329,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
} }
// convert to primary, but is NOT already primary. // convert to primary, but is NOT already primary.
if ($this->convertToPrimary && (int)$entry->transaction_currency_id !== $this->primaryCurrency->id) { if ($this->convertToPrimary && (int)$entry->transaction_currency_id !== $this->primaryCurrency->id) {
$array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, $entry->amount); $array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, (string) $entry->amount);
} }
// convert to primary, but foreign is already primary. // convert to primary, but foreign is already primary.
if ($this->convertToPrimary && (int)$entry->foreign_currency_id === $this->primaryCurrency->id) { if ($this->convertToPrimary && (int)$entry->foreign_currency_id === $this->primaryCurrency->id) {
@@ -340,7 +340,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
// TODO this is very database intensive. // TODO this is very database intensive.
/** @var TransactionCurrency $foreignCurrency */ /** @var TransactionCurrency $foreignCurrency */
$foreignCurrency = Amount::getTransactionCurrencyById($entry->foreign_currency_id); $foreignCurrency = Amount::getTransactionCurrencyById($entry->foreign_currency_id);
$array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount); $array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, (string) $entry->amount);
} }
$result[] = $array; $result[] = $array;
} }

View File

@@ -34,6 +34,9 @@
"transfers", "transfers",
"management" "management"
], ],
"platform": {
"php": "8.4"
},
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"homepage": "https://github.com/firefly-iii/firefly-iii", "homepage": "https://github.com/firefly-iii/firefly-iii",
"type": "project", "type": "project",

14
composer.lock generated
View File

@@ -3621,16 +3621,16 @@
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "3.9.0", "version": "3.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/monolog.git", "url": "https://github.com/Seldaek/monolog.git",
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0",
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3648,7 +3648,7 @@
"graylog2/gelf-php": "^1.4.2 || ^2.0", "graylog2/gelf-php": "^1.4.2 || ^2.0",
"guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/guzzle": "^7.4.5",
"guzzlehttp/psr7": "^2.2", "guzzlehttp/psr7": "^2.2",
"mongodb/mongodb": "^1.8", "mongodb/mongodb": "^1.8 || ^2.0",
"php-amqplib/php-amqplib": "~2.4 || ^3", "php-amqplib/php-amqplib": "~2.4 || ^3",
"php-console/php-console": "^3.1.8", "php-console/php-console": "^3.1.8",
"phpstan/phpstan": "^2", "phpstan/phpstan": "^2",
@@ -3708,7 +3708,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/Seldaek/monolog/issues", "issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/3.9.0" "source": "https://github.com/Seldaek/monolog/tree/3.10.0"
}, },
"funding": [ "funding": [
{ {
@@ -3720,7 +3720,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-03-24T10:02:05+00:00" "time": "2026-01-02T08:56:05+00:00"
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used. 'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
// see cer.php for exchange rates feature flag. // see cer.php for exchange rates feature flag.
], ],
'version' => 'develop/2026-01-01', 'version' => 'develop/2026-01-05',
'build_time' => 1767271288, 'build_time' => 1767598210,
'api_version' => '2.1.0', // field is no longer used. 'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used. 'db_version' => 28, // field is no longer used.

View File

@@ -43,6 +43,7 @@ return [
'last_180_days' => 'Last 180 days', 'last_180_days' => 'Last 180 days',
'month_to_date' => 'Month to date', 'month_to_date' => 'Month to date',
'year_to_date' => 'Year to date', 'year_to_date' => 'Year to date',
'previous_year' => 'Previous year (:year)',
'YTD' => 'YTD', 'YTD' => 'YTD',
'welcome_back' => 'What\'s playing?', 'welcome_back' => 'What\'s playing?',
'main_dashboard_page_title' => 'Home', 'main_dashboard_page_title' => 'Home',