diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cc97bc569c..f715742ead 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,14 +6,6 @@ I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). -## Bugs - -First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority! - -## Installation problems - -Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can. - ## Pull requests When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `master` branch. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000000..7b7759dcb8 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,11 @@ +# Welcome to Firefly III on Github! + +:+1::tada: Thank you for taking the time to contribute something to Firefly III! + +## Bugs + +First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority! + +## Installation problems + +Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can. \ No newline at end of file diff --git a/.gitignore b/.gitignore index b1e27bfc74..d4cfc0d4fb 100755 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ Homestead.json Homestead.yaml .env public/google*.html +report.html diff --git a/CHANGELOG.md b/CHANGELOG.md index f68ff4250a..9c1e52cae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). + +## [4.6.3] - 2017-07-23 + +This will be the last release to support PHP 7.0. + +### Added +- New guidelines and new introduction tour to aid new users. +- Rules can now be applied at will to transactions, not just rule groups. + +### Changed +- Improved category overview. +- Improved budget overview. +- Improved budget report. +- Improved command line import responsiveness and speed. +- All code comparisons are now strict. +- Improve search page. +- Charts are easier to read thanks to @simonsmiley +- Fixed #708. + +### Fixed +- Fixed bug where import would not respect default account. #694 +- Fixed various broken paths +- Fixed several import inconsistencies. +- Various bug fixes. + +### Security +- Initial release. + + + ## [4.6.2] - 2017-07-08 ### Added - Links added to boxes, idea by @simonsmiley diff --git a/app/Console/Commands/CreateImport.php b/app/Console/Commands/CreateImport.php index 15ed5cd468..f155d9add0 100644 --- a/app/Console/Commands/CreateImport.php +++ b/app/Console/Commands/CreateImport.php @@ -14,10 +14,14 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands; use Artisan; +use FireflyIII\Import\Logging\CommandHandler; +use FireflyIII\Import\Routine\ImportRoutine; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Console\Command; +use Illuminate\Support\MessageBag; use Log; +use Monolog\Formatter\LineFormatter; /** * Class CreateImport @@ -51,6 +55,7 @@ class CreateImport extends Command /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly. */ public function handle() { @@ -73,30 +78,55 @@ class CreateImport extends Command return; } - $this->info(sprintf('Going to create a job to import file: %s', $file)); - $this->info(sprintf('Using configuration file: %s', $configuration)); - $this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email)); - $this->info(sprintf('Type of import: %s', $type)); + $this->line(sprintf('Going to create a job to import file: %s', $file)); + $this->line(sprintf('Using configuration file: %s', $configuration)); + $this->line(sprintf('Import into user: #%d (%s)', $user->id, $user->email)); + $this->line(sprintf('Type of import: %s', $type)); + /** @var ImportJobRepositoryInterface $jobRepository */ $jobRepository = app(ImportJobRepositoryInterface::class); $jobRepository->setUser($user); $job = $jobRepository->create($type); - $this->line(sprintf('Created job "%s"...', $job->key)); + $this->line(sprintf('Created job "%s"', $job->key)); + Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]); $this->line('Stored import data...'); + $job->configuration = $configurationData; $job->status = 'configured'; $job->save(); $this->line('Stored configuration...'); + if ($this->option('start') === true) { $this->line('The import will start in a moment. This process is not visible...'); Log::debug('Go for import!'); - Artisan::call('firefly:start-import', ['key' => $job->key]); - $this->line('Done!'); + + // normally would refer to other firefly:start-import but that doesn't seem to work all to well... + $monolog = Log::getMonolog(); + $handler = new CommandHandler($this); + $formatter = new LineFormatter(null, null, false, true); + $handler->setFormatter($formatter); + $monolog->pushHandler($handler); + + + // start the actual routine: + /** @var ImportRoutine $routine */ + $routine = app(ImportRoutine::class); + $routine->setJob($job); + $routine->run(); + + // give feedback. + /** @var MessageBag $error */ + foreach ($routine->errors as $index => $error) { + $this->error(sprintf('Error importing line #%d: %s', $index, $error)); + } + $this->line( + sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines) + ); } return; diff --git a/app/Console/Commands/UpgradeDatabase.php b/app/Console/Commands/UpgradeDatabase.php index 42566a6cee..066e4496d3 100644 --- a/app/Console/Commands/UpgradeDatabase.php +++ b/app/Console/Commands/UpgradeDatabase.php @@ -36,6 +36,7 @@ use Steam; /** * Class UpgradeDatabase + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things. * * @package FireflyIII\Console\Commands */ @@ -80,6 +81,8 @@ class UpgradeDatabase extends Command /** * Moves the currency id info to the transaction instead of the journal. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped. */ private function currencyInfoToTransactions() { @@ -96,7 +99,6 @@ class UpgradeDatabase extends Command } } - // read and use the foreign amounts when present. if ($journal->hasMeta('foreign_amount')) { $amount = Steam::positive($journal->getMeta('foreign_amount')); @@ -123,6 +125,8 @@ class UpgradeDatabase extends Command /** * Migrate budget repetitions to new format. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 5. */ private function migrateRepetitions() { @@ -150,6 +154,8 @@ class UpgradeDatabase extends Command /** * Make sure there are only transfers linked to piggy bank events. + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped. */ private function repairPiggyBanks() { @@ -157,6 +163,7 @@ class UpgradeDatabase extends Command if (!Schema::hasTable('piggy_bank_events')) { return; } + $set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get(); /** @var PiggyBankEvent $event */ foreach ($set as $event) { @@ -208,7 +215,7 @@ class UpgradeDatabase extends Command } /** - * + * Make sure all accounts have proper currency info. */ private function updateAccountCurrencies() { @@ -218,42 +225,38 @@ class UpgradeDatabase extends Command /** @var Account $account */ foreach ($accounts as $account) { // get users preference, fall back to system pref. - $defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data; - $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); - $accountCurrency = intval($account->getMeta('currency_id')); - $openingBalance = $account->getOpeningBalance(); - $openingBalanceCurrency = intval($openingBalance->transaction_currency_id); + $defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data; + $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); + $accountCurrency = intval($account->getMeta('currency_id')); + $openingBalance = $account->getOpeningBalance(); + $obCurrency = intval($openingBalance->transaction_currency_id); // both 0? set to default currency: - if ($accountCurrency === 0 && $openingBalanceCurrency === 0) { + if ($accountCurrency === 0 && $obCurrency === 0) { AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]); $this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode)); continue; } - // opening balance 0, account not zero? just continue: - if ($accountCurrency > 0 && $openingBalanceCurrency === 0) { - continue; - } // account is set to 0, opening balance is not? - if ($accountCurrency === 0 && $openingBalanceCurrency > 0) { - AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $openingBalanceCurrency]); + if ($accountCurrency === 0 && $obCurrency > 0) { + AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]); $this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode)); continue; } - // both are equal, just continue: - if ($accountCurrency === $openingBalanceCurrency) { - continue; - } // do not match: - if ($accountCurrency !== $openingBalanceCurrency) { + if ($accountCurrency !== $obCurrency) { // update opening balance: $openingBalance->transaction_currency_id = $accountCurrency; $openingBalance->save(); $this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name)); continue; } + + // opening balance 0, account not zero? just continue: + // both are equal, just continue: + } } diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index f98e84f86a..363e5089f7 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -50,10 +50,10 @@ class UpgradeFireflyInstructions extends Command public function handle() { - if ($this->argument('task') == 'update') { + if ($this->argument('task') === 'update') { $this->updateInstructions(); } - if ($this->argument('task') == 'install') { + if ($this->argument('task') === 'install') { $this->installInstructions(); } } diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 52cf80c265..6e128e7f53 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -259,7 +259,7 @@ class VerifyDatabase extends Command { $plural = str_plural($name); $class = sprintf('FireflyIII\Models\%s', ucfirst($name)); - $field = $name == 'tag' ? 'tag' : 'name'; + $field = $name === 'tag' ? 'tag' : 'name'; $set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id') ->leftJoin('users', $plural . '.user_id', '=', 'users.id') ->distinct() diff --git a/app/Export/Entry/Entry.php b/app/Export/Entry/Entry.php index f4061f703b..13be79458e 100644 --- a/app/Export/Entry/Entry.php +++ b/app/Export/Entry/Entry.php @@ -88,7 +88,7 @@ final class Entry $entry->budget_name = $object->budget_name ?? ''; // update description when transaction description is different: - if (!is_null($object->description) && $object->description != $entry->description) { + if (!is_null($object->description) && $object->description !== $entry->description) { $entry->description = $entry->description . ' (' . $object->description . ')'; } diff --git a/app/Generator/Chart/Basic/ChartJsGenerator.php b/app/Generator/Chart/Basic/ChartJsGenerator.php index 4649e0722d..b8086880f4 100644 --- a/app/Generator/Chart/Basic/ChartJsGenerator.php +++ b/app/Generator/Chart/Basic/ChartJsGenerator.php @@ -110,7 +110,14 @@ class ChartJsGenerator implements GeneratorInterface ]; // sort by value, keep keys. + // different sort when values are positive and when they're negative. asort($data); + $next = next($data); + if (!is_bool($next) && bccomp($next, '0') === 1) { + // next is positive, sort other way around. + arsort($data); + } + unset($next); $index = 0; foreach ($data as $key => $value) { diff --git a/app/Helpers/Collection/BalanceLine.php b/app/Helpers/Collection/BalanceLine.php index 73b1a0f486..c3d892b214 100644 --- a/app/Helpers/Collection/BalanceLine.php +++ b/app/Helpers/Collection/BalanceLine.php @@ -147,13 +147,13 @@ class BalanceLine if ($this->getBudget() instanceof BudgetModel && !is_null($this->getBudget()->id)) { return $this->getBudget()->name; } - if ($this->getRole() == self::ROLE_DEFAULTROLE) { + if ($this->getRole() === self::ROLE_DEFAULTROLE) { return strval(trans('firefly.no_budget')); } - if ($this->getRole() == self::ROLE_TAGROLE) { + if ($this->getRole() === self::ROLE_TAGROLE) { return strval(trans('firefly.coveredWithTags')); } - if ($this->getRole() == self::ROLE_DIFFROLE) { + if ($this->getRole() === self::ROLE_DIFFROLE) { return strval(trans('firefly.leftUnbalanced')); } diff --git a/app/Helpers/Collection/Bill.php b/app/Helpers/Collection/Bill.php index e78e31631b..0df4a5fd5b 100644 --- a/app/Helpers/Collection/Bill.php +++ b/app/Helpers/Collection/Bill.php @@ -96,7 +96,7 @@ class Bill { $set = $this->bills->sortBy( function (BillLine $bill) { - $active = intval($bill->getBill()->active) == 0 ? 1 : 0; + $active = intval($bill->getBill()->active) === 0 ? 1 : 0; $name = $bill->getBill()->name; return $active . $name; diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 7edc9a5164..0fcecc781b 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -89,8 +89,6 @@ class JournalCollector implements JournalCollectorInterface 'account_types.type as account_type', ]; /** @var bool */ - private $filterInternalTransfers; - /** @var bool */ private $filterTransfers = false; /** @var array */ private $filters = [InternalTransferFilter::class]; @@ -508,7 +506,8 @@ class JournalCollector implements JournalCollectorInterface ->where('transaction_journals.user_id', $this->user->id) ->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC'); + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC'); $this->query = $query; diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index c7c97bcb96..c1b1af2293 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -27,6 +27,7 @@ use Route; */ class Help implements HelpInterface { + const CACHEKEY = 'help_%s_%s'; /** @var string */ protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'; @@ -38,7 +39,7 @@ class Help implements HelpInterface */ public function getFromCache(string $route, string $language): string { - $line = sprintf('help.%s.%s', $route, $language); + $line = sprintf(self::CACHEKEY, $route, $language); return Cache::get($line); } @@ -64,12 +65,12 @@ class Help implements HelpInterface return ''; } - Log::debug(sprintf('Status code is %d', $result->status_code)); if ($result->status_code === 200) { $content = trim($result->body); } + if (strlen($content) > 0) { Log::debug('Content is longer than zero. Expect something.'); $converter = new CommonMarkConverter(); @@ -98,7 +99,7 @@ class Help implements HelpInterface */ public function inCache(string $route, string $language): bool { - $line = sprintf('help.%s.%s', $route, $language); + $line = sprintf(self::CACHEKEY, $route, $language); $result = Cache::has($line); if ($result) { Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language)); @@ -120,7 +121,7 @@ class Help implements HelpInterface */ public function putInCache(string $route, string $language, string $content) { - $key = sprintf('help.%s.%s', $route, $language); + $key = sprintf(self::CACHEKEY, $route, $language); if (strlen($content) > 0) { Log::debug(sprintf('Will store entry in cache: %s', $key)); Cache::put($key, $content, 10080); // a week. diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index 9c62ea1ebc..dadef8fe41 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -244,7 +244,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface foreach ($accounts as $account) { $leftEntry = $tagsLeft->filter( function (Tag $tag) use ($account) { - return $tag->account_id == $account->id; + return $tag->account_id === $account->id; } ); $left = '0'; diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 55c6ea64a7..7fecb90c89 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -56,7 +56,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface /** @var Budget $budget */ foreach ($set as $budget) { $budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end); - if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget + if ($budgetLimits->count() === 0) { // no budget limit(s) for this budget $spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range if (bccomp($spent, '0') === -1) { diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index a76de3ace7..a93b01444d 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -194,7 +194,7 @@ class AccountController extends Controller return view( 'accounts.edit', compact( - 'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles' + 'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles' ) ); } @@ -316,7 +316,7 @@ class AccountController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 3f06091f35..6827d58fe2 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -153,7 +153,7 @@ class AttachmentController extends Controller $image = 'images/page_green.png'; - if ($attachment->mime == 'application/pdf') { + if ($attachment->mime === 'application/pdf') { $image = 'images/page_white_acrobat.png'; } $file = public_path($image); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 2418bde1b0..8a963cd57f 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -175,7 +175,7 @@ class BillController extends Controller */ public function rescan(Request $request, BillRepositoryInterface $repository, Bill $bill) { - if (intval($bill->active) == 0) { + if (intval($bill->active) === 0) { $request->session()->flash('warning', strval(trans('firefly.cannot_scan_inactive_bill'))); return redirect(URL::previous()); @@ -206,7 +206,7 @@ class BillController extends Controller /** @var Carbon $date */ $date = session('start'); $year = $date->year; - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $yearAverage = $repository->getYearAverage($bill, $date); $overallAverage = $repository->getOverallAverage($bill); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index f9d6bd72a7..b223c477dc 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -81,7 +81,7 @@ class BudgetController extends Controller /** @var Carbon $end */ $end = session('end', Carbon::now()->endOfMonth()); $budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount); - if ($amount == 0) { + if ($amount === 0) { $budgetLimit = null; } Preferences::mark(); @@ -293,7 +293,7 @@ class BudgetController extends Controller ); } - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $count = 0; @@ -318,7 +318,7 @@ class BudgetController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.without_budget_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] @@ -357,7 +357,7 @@ class BudgetController extends Controller /** @var Carbon $start */ $start = session('first', Carbon::create()->startOfYear()); $end = new Carbon; - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $limits = $this->getLimits($budget, $start, $end); $repetition = null; @@ -384,11 +384,11 @@ class BudgetController extends Controller */ public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit) { - if ($budgetLimit->budget->id != $budget->id) { + if ($budgetLimit->budget->id !== $budget->id) { throw new FireflyException('This budget limit is not part of this budget.'); } - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans( 'firefly.budget_in_period', [ diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index c9aec777f4..4cf4389a2a 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -199,7 +199,7 @@ class CategoryController extends Controller ); } - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $count = 0; @@ -224,7 +224,7 @@ class CategoryController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.without_category_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] @@ -247,7 +247,7 @@ class CategoryController extends Controller // default values: $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $count = 0; $loop = 0; @@ -308,7 +308,7 @@ class CategoryController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.journals_in_period_for_category', ['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat), @@ -463,7 +463,7 @@ class CategoryController extends Controller $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $first = $repository->firstUseDate($category); - if ($first->year == 1900) { + if ($first->year === 1900) { $first = new Carbon; } $range = Preferences::get('viewRange', '1M')->data; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 8a0d923231..4b74cfbf38 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -124,7 +124,7 @@ class BudgetController extends Controller */ public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit) { - if ($budgetLimit->budget->id != $budget->id) { + if ($budgetLimit->budget->id !== $budget->id) { throw new FireflyException('This budget limit is not part of this budget.'); } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index e4301ee034..b6ac5e0973 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -67,7 +67,7 @@ class CategoryController extends Controller $start = $repository->firstUseDate($category); - if ($start->year == 1900) { + if ($start->year === 1900) { $start = new Carbon; } diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 653996cf5d..a79073c863 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -247,7 +247,7 @@ class CategoryReportController extends Controller // remove all empty entries to prevent cluttering: $newSet = []; foreach ($chartData as $key => $entry) { - if (!array_sum($entry['entries']) == 0) { + if (!array_sum($entry['entries']) === 0) { $newSet[$key] = $chartData[$key]; } } diff --git a/app/Http/Controllers/Chart/TagReportController.php b/app/Http/Controllers/Chart/TagReportController.php index 5c3ee98ed1..d7e86825b6 100644 --- a/app/Http/Controllers/Chart/TagReportController.php +++ b/app/Http/Controllers/Chart/TagReportController.php @@ -231,7 +231,7 @@ class TagReportController extends Controller // remove all empty entries to prevent cluttering: $newSet = []; foreach ($chartData as $key => $entry) { - if (!array_sum($entry['entries']) == 0) { + if (!array_sum($entry['entries']) === 0) { $newSet[$key] = $chartData[$key]; } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 63cbe9c975..ed1835d9c9 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -18,10 +18,13 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\Support\Facades\Preferences; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use Log; +use Route; use Session; use URL; use View; @@ -47,22 +50,50 @@ class Controller extends BaseController */ public function __construct() { + // for transaction lists: View::share('hideBudgets', false); View::share('hideCategories', false); View::share('hideBills', false); View::share('hideTags', false); + + // is site a demo site? $isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data; View::share('IS_DEMO_SITE', $isDemoSite); View::share('DEMO_USERNAME', env('DEMO_USERNAME', '')); View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', '')); - // translations: + $this->middleware( function ($request, $next) { + // translations for specific strings: $this->monthFormat = (string)trans('config.month'); $this->monthAndDayFormat = (string)trans('config.month_and_day'); $this->dateTimeFormat = (string)trans('config.date_time'); + // get shown-intro-preference: + if (auth()->check()) { + // some routes have a "what" parameter, which indicates a special page: + $specificPage = is_null(Route::current()->parameter('what')) ? '' : '_' . Route::current()->parameter('what'); + $page = str_replace('.', '_', Route::currentRouteName()); + + // indicator if user has seen the help for this page ( + special page): + $key = 'shown_demo_' . $page . $specificPage; + // is there an intro for this route? + $intro = config('intro.' . $page); + $specialIntro = config('intro.' . $page . $specificPage); + $shownDemo = true; + + // either must be array and either must be > 0 + if ((is_array($intro) || is_array($specialIntro)) && (count($intro) > 0 || count($specialIntro) > 0)) { + $shownDemo = Preferences::get($key, false)->data; + Log::debug(sprintf('Check if user has already seen intro with key "%s". Result is %d', $key, $shownDemo)); + } + + View::share('shownDemo', $shownDemo); + View::share('current_route_name', $page); + View::share('original_route_name', Route::currentRouteName()); + } + return $next($request); } ); diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index e776749bc6..260b214b8f 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -25,62 +25,95 @@ use Response; */ class HelpController extends Controller { + + /** @var HelpInterface */ + private $help; + /** * HelpController constructor. */ public function __construct() { parent::__construct(); + + $this->middleware( + function ($request, $next) { + $this->help = app(HelpInterface::class); + + return $next($request); + } + ); } /** - * @param HelpInterface $help * @param $route * * @return \Illuminate\Http\JsonResponse */ - public function show(HelpInterface $help, string $route) + public function show(string $route) { - $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; - $content = '
' . strval(trans('firefly.route_has_no_help')) . '
'; + $html = $this->getHelpText($route, $language); - if (!$help->hasRoute($route)) { + return Response::json(['html' => $html]); + + } + + /** + * @param string $route + * @param string $language + * + * @return string + */ + private function getHelpText(string $route, string $language): string + { + // get language and default variables. + + $content = '' . strval(trans('firefly.route_has_no_help')) . '
'; + + // if no such route, log error and return default text. + if (!$this->help->hasRoute($route)) { Log::error('No such route: ' . $route); - return Response::json($content); + return $content; } - if ($help->inCache($route, $language)) { - $content = $help->getFromCache($route, $language); + // help content may be cached: + if ($this->help->inCache($route, $language)) { + $content = $this->help->getFromCache($route, $language); Log::debug(sprintf('Help text %s was in cache.', $language)); - return Response::json($content); + return $content; } - $content = $help->getFromGithub($route, $language); - $notYourLanguage = '' . strval(trans('firefly.help_may_not_be_your_language')) . '
'; + // get help content from Github: + $content = $this->help->getFromGithub($route, $language); - // get backup language content (try English): + // content will have 0 length when Github failed. Try en_US when it does: if (strlen($content) === 0) { $language = 'en_US'; - if ($help->inCache($route, $language)) { + + // also check cache first: + if ($this->help->inCache($route, $language)) { Log::debug(sprintf('Help text %s was in cache.', $language)); - $content = $notYourLanguage . $help->getFromCache($route, $language); - } - if (!$help->inCache($route, $language)) { - $content = trim($notYourLanguage . $help->getFromGithub($route, $language)); + $content = $this->help->getFromCache($route, $language); + + return $content; } + + $content = $this->help->getFromGithub($route, $language); + } - if ($content === $notYourLanguage) { - $content = '' . strval(trans('firefly.route_has_no_help')) . '
'; + // help still empty? + if (strlen($content) !== 0) { + $this->help->putInCache($route, $language, $content); + + return $content; + } - $help->putInCache($route, $language, $content); - - return Response::json($content); - + return '' . strval(trans('firefly.route_has_no_help')) . '
'; } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 7e4cca9547..73c0d4dfe6 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -21,9 +21,11 @@ use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Illuminate\Http\Request; +use Illuminate\Routing\Route; use Illuminate\Support\Collection; use Log; use Preferences; +use Route as RouteFacade; use Session; use View; @@ -107,7 +109,7 @@ class HomeController extends Controller $types = config('firefly.accountTypesByIdentifier.asset'); $count = $repository->count($types); - if ($count == 0) { + if ($count === 0) { return redirect(route('new-user.index')); } @@ -120,7 +122,6 @@ class HomeController extends Controller $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ $end = session('end', Carbon::now()->endOfMonth()); - $showTour = Preferences::get('tour', true)->data; $accounts = $repository->getAccountsById($frontPage->data); $showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data; @@ -137,10 +138,34 @@ class HomeController extends Controller } return view( - 'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount') + 'index', compact('count', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount') ); } + public function routes() + { + $set = RouteFacade::getRoutes(); + $ignore = ['chart.', 'javascript.', 'json.', 'report-data.', 'popup.', 'debugbar.']; + /** @var Route $route */ + foreach ($set as $route) { + $name = $route->getName(); + if (!is_null($name) && in_array('GET', $route->methods()) && strlen($name) > 0) { + $found = false; + foreach ($ignore as $string) { + if (strpos($name, $string) !== false) { + $found = true; + } + } + if (!$found) { + echo 'touch ' . $route->getName() . '.md;'; + } + + } + } + + return ' '; + } + /** * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index c4b0bd1342..d49835f280 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -92,7 +92,7 @@ class ImportController extends Controller Log::debug('Now in download()', ['job' => $job->key]); $config = $job->configuration; - // TODO this is CSV import specific: + // This is CSV import specific: $config['column-roles-complete'] = false; $config['column-mapping-complete'] = false; $config['initial-config-complete'] = false; diff --git a/app/Http/Controllers/Json/IntroController.php b/app/Http/Controllers/Json/IntroController.php new file mode 100644 index 0000000000..c86ac5a8ad --- /dev/null +++ b/app/Http/Controllers/Json/IntroController.php @@ -0,0 +1,166 @@ +getBasicSteps($route); + $specificSteps = $this->getSpecificSteps($route, $specificPage); + if (count($specificSteps) === 0) { + return Response::json($steps); + } + if ($this->hasOutroStep($route)) { + // save last step: + $lastStep = $steps[count($steps) - 1]; + // remove last step: + array_pop($steps); + // merge arrays and add last step again + $steps = array_merge($steps, $specificSteps); + $steps[] = $lastStep; + + } + if (!$this->hasOutroStep($route)) { + $steps = array_merge($steps, $specificSteps); + } + + return Response::json($steps); + } + + /** + * @param string $route + * + * @return bool + */ + public function hasOutroStep(string $route): bool + { + $routeKey = str_replace('.', '_', $route); + $elements = config(sprintf('intro.%s', $routeKey)); + if (!is_array($elements)) { + return false; + } + $keys = array_keys($elements); + + return in_array('outro', $keys); + } + + /** + * @param string $route + * @param string $specialPage + * + * @return \Illuminate\Http\JsonResponse + */ + public function postEnable(string $route, string $specialPage = '') + { + $route = str_replace('.', '_', $route); + $key = 'shown_demo_' . $route; + if ($specialPage !== '') { + $key .= '_' . $specialPage; + } + Log::debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key)); + Preferences::set($key, false); + + return Response::json(['message' => trans('firefly.intro_boxes_after_refresh')]); + } + + /** + * @param string $route + * @param string $specialPage + * + * @return \Illuminate\Http\JsonResponse + */ + public function postFinished(string $route, string $specialPage = '') + { + $key = 'shown_demo_' . $route; + if ($specialPage !== '') { + $key .= '_' . $specialPage; + } + Log::debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key)); + Preferences::set($key, true); + + return Response::json(['result' => sprintf('Reported demo watched for route "%s".', $route)]); + } + + /** + * @param string $route + * + * @return array + */ + private function getBasicSteps(string $route): array + { + $routeKey = str_replace('.', '_', $route); + $elements = config(sprintf('intro.%s', $routeKey)); + $steps = []; + if (is_array($elements) && count($elements) > 0) { + foreach ($elements as $key => $options) { + $currentStep = $options; + + // get the text: + $currentStep['intro'] = trans('intro.' . $route . '_' . $key); + + + // save in array: + $steps[] = $currentStep; + } + } + + return $steps; + } + + /** + * @param string $route + * @param string $specificPage + * + * @return array + */ + private function getSpecificSteps(string $route, string $specificPage): array + { + $steps = []; + + // user is on page with specific instructions: + if (strlen($specificPage) > 0) { + $routeKey = str_replace('.', '_', $route); + $elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage)); + if (is_array($elements) && count($elements) > 0) { + foreach ($elements as $key => $options) { + $currentStep = $options; + + // get the text: + $currentStep['intro'] = trans('intro.' . $route . '_' . $specificPage . '_' . $key); + + // save in array: + $steps[] = $currentStep; + } + } + } + + return $steps; + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index e86536bc7b..849b0631f9 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -15,7 +15,6 @@ namespace FireflyIII\Http\Controllers; use Amount; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionType; @@ -27,7 +26,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Http\Request; -use Preferences; use Response; /** @@ -237,16 +235,6 @@ class JsonController extends Controller return Response::json($return); } - /** - * @return \Illuminate\Http\JsonResponse - */ - public function endTour() - { - Preferences::set('tour', false); - - return Response::json('true'); - } - /** * Returns a JSON list of all beneficiaries. * @@ -293,34 +281,6 @@ class JsonController extends Controller } - /** - * - */ - public function tour() - { - $pref = Preferences::get('tour', true); - if (!$pref) { - throw new FireflyException('Cannot find preference for tour. Exit.'); // @codeCoverageIgnore - } - $headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end']; - $steps = []; - foreach ($headers as $header) { - $steps[] = [ - 'element' => '#' . $header, - 'title' => trans('help.' . $header . '-title'), - 'content' => trans('help.' . $header . '-text'), - ]; - } - $steps[0]['orphan'] = true;// orphan and backdrop for first element. - $steps[0]['backdrop'] = true; - $steps[1]['placement'] = 'left';// sidebar position left: - $steps[7]['orphan'] = true; // final in the center again. - $steps[7]['backdrop'] = true; - $template = view('json.tour')->render(); - - return Response::json(['steps' => $steps, 'template' => $template]); - } - /** * @param JournalCollectorInterface $collector * @param string $what @@ -365,7 +325,7 @@ class JsonController extends Controller $keys = array_keys(config('firefly.rule-triggers')); $triggers = []; foreach ($keys as $key) { - if ($key != 'user_action') { + if ($key !== 'user_action') { $triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice'); } } diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 478e5830d6..8e47d8a13b 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -54,7 +54,6 @@ class NewUserController extends Controller View::share('title', trans('firefly.welcome')); View::share('mainTitleIcon', 'fa-fire'); - $types = config('firefly.accountTypesByIdentifier.asset'); $count = $repository->count($types); @@ -74,30 +73,13 @@ class NewUserController extends Controller */ public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository) { - $count = 1; // create normal asset account: $this->createAssetAccount($request, $repository); // create savings account - $savingBalance = strval($request->get('savings_balance')) === '' ? '0' : strval($request->get('savings_balance')); - if (bccomp($savingBalance, '0') !== 0) { - $this->createSavingsAccount($request, $repository); - $count++; - } + $this->createSavingsAccount($request, $repository); - - // create credit card. - $limit = strval($request->get('credit_card_limit')) === '' ? '0' : strval($request->get('credit_card_limit')); - if (bccomp($limit, '0') !== 0) { - $this->storeCreditCard($request, $repository); - $count++; - } - $message = strval(trans('firefly.stored_new_accounts_new_user')); - if ($count == 1) { - $message = strval(trans('firefly.stored_new_account_new_user')); - } - - Session::flash('success', $message); + Session::flash('success', strval(trans('firefly.stored_new_accounts_new_user'))); Preferences::mark(); return redirect(route('index')); @@ -152,29 +134,4 @@ class NewUserController extends Controller return true; } - /** - * @param NewUserFormRequest $request - * @param AccountRepositoryInterface $repository - * - * @return bool - */ - private function storeCreditCard(NewUserFormRequest $request, AccountRepositoryInterface $repository): bool - { - $creditAccount = [ - 'name' => 'Credit card', - 'iban' => null, - 'accountType' => 'asset', - 'virtualBalance' => round($request->get('credit_card_limit'), 12), - 'active' => true, - 'accountRole' => 'ccAsset', - 'openingBalance' => null, - 'openingBalanceDate' => null, - 'openingBalanceCurrency' => intval($request->input('amount_currency_id_credit_card_limit')), - 'ccType' => 'monthlyFull', - 'ccMonthlyPaymentDate' => Carbon::now()->year . '-01-01', - ]; - $repository->store($creditAccount); - - return true; - } } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 207f98cd6f..c9fbe8dba4 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -89,7 +89,7 @@ class PiggyBankController extends Controller /** @var Carbon $date */ $date = session('end', Carbon::now()->endOfMonth()); $leftOnAccount = $piggyBank->leftOnAccount($date); - $savedSoFar = $piggyBank->currentRelevantRep()->currentamount?? '0'; + $savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0'; $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); $maxAmount = min($leftOnAccount, $leftToSave); diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index ba4dc6d208..46e1d3f8ed 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -13,12 +13,18 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; +use Carbon\Carbon; +use ExpandedForm; use FireflyIII\Http\Requests\RuleFormRequest; +use FireflyIII\Http\Requests\SelectTransactionsRequest; use FireflyIII\Http\Requests\TestRuleFormRequest; +use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions; +use FireflyIII\Models\AccountType; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Rules\TransactionMatcher; @@ -188,6 +194,41 @@ class RuleController extends Controller return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); } + /** + * Execute the given rule on a set of existing transactions + * + * @param SelectTransactionsRequest $request + * @param AccountRepositoryInterface $repository + * @param Rule $rule + * + * @return \Illuminate\Http\RedirectResponse + * @internal param RuleGroup $ruleGroup + */ + public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, Rule $rule) + { + // Get parameters specified by the user + $accounts = $repository->getAccountsById($request->get('accounts')); + $startDate = new Carbon($request->get('start_date')); + $endDate = new Carbon($request->get('end_date')); + + // Create a job to do the work asynchronously + $job = new ExecuteRuleOnExistingTransactions($rule); + + // Apply parameters to the job + $job->setUser(auth()->user()); + $job->setAccounts($accounts); + $job->setStartDate($startDate); + $job->setEndDate($endDate); + + // Dispatch a new job to execute it in a queue + $this->dispatch($job); + + // Tell the user that the job is queued + Session::flash('success', strval(trans('firefly.applied_rule_selection', ['title' => $rule->title]))); + + return redirect()->route('rules.index'); + } + /** * @param RuleGroupRepositoryInterface $repository * @@ -238,6 +279,25 @@ class RuleController extends Controller } + /** + * @param AccountRepositoryInterface $repository + * @param Rule $rule + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function selectTransactions(AccountRepositoryInterface $repository, Rule $rule) + { + // does the user have shared accounts? + $accounts = $repository->getAccountsByType([AccountType::ASSET]); + $accountList = ExpandedForm::makeSelectList($accounts); + $checkedAccounts = array_keys($accountList); + $first = session('first')->format('Y-m-d'); + $today = Carbon::create()->format('Y-m-d'); + $subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]); + + return view('rules.rule.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'rule', 'subTitle')); + } + /** * @param RuleFormRequest $request * @param RuleRepositoryInterface $repository @@ -282,7 +342,7 @@ class RuleController extends Controller // build trigger array from response $triggers = $this->getValidTriggerList($request); - if (count($triggers) == 0) { + if (count($triggers) === 0) { return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]); } @@ -294,14 +354,61 @@ class RuleController extends Controller $matcher->setLimit($limit); $matcher->setRange($range); $matcher->setTriggers($triggers); - $matchingTransactions = $matcher->findMatchingTransactions(); + $matchingTransactions = $matcher->findTransactionsByTriggers(); // Warn the user if only a subset of transactions is returned $warning = ''; - if (count($matchingTransactions) == $limit) { + if (count($matchingTransactions) === $limit) { $warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); } - if (count($matchingTransactions) == 0) { + if (count($matchingTransactions) === 0) { + $warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); + } + + // Return json response + $view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); + + return Response::json(['html' => $view, 'warning' => $warning]); + } + + /** + * This method allows the user to test a certain set of rule triggers. The rule triggers are grabbed from + * the rule itself. + * + * This method will parse and validate those rules and create a "TransactionMatcher" which will attempt + * to find transaction journals matching the users input. A maximum range of transactions to try (range) and + * a maximum number of transactions to return (limit) are set as well. + * + * + * @param Rule $rule + * + * @return \Illuminate\Http\JsonResponse + */ + public function testTriggersByRule(Rule $rule) + { + + $triggers = $rule->ruleTriggers; + + if (count($triggers) === 0) { + return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]); + } + + $limit = config('firefly.test-triggers.limit'); + $range = config('firefly.test-triggers.range'); + + /** @var TransactionMatcher $matcher */ + $matcher = app(TransactionMatcher::class); + $matcher->setLimit($limit); + $matcher->setRange($range); + $matcher->setRule($rule); + $matchingTransactions = $matcher->findTransactionsByRule(); + + // Warn the user if only a subset of transactions is returned + $warning = ''; + if (count($matchingTransactions) === $limit) { + $warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); + } + if (count($matchingTransactions) === 0) { $warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); } @@ -440,7 +547,7 @@ class RuleController extends Controller /** @var RuleTrigger $entry */ foreach ($rule->ruleTriggers as $entry) { - if ($entry->trigger_type != 'user_action') { + if ($entry->trigger_type !== 'user_action') { $count = ($index + 1); $triggers[] = view( 'rules.partials.trigger', diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 12f9a0bb58..192042172e 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -178,7 +178,7 @@ class RuleGroupController extends Controller $this->dispatch($job); // Tell the user that the job is queued - Session::flash('success', strval(trans('firefly.executed_group_on_existing_transactions', ['title' => $ruleGroup->title]))); + Session::flash('success', strval(trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title]))); return redirect()->route('rules.index'); } @@ -197,7 +197,7 @@ class RuleGroupController extends Controller $checkedAccounts = array_keys($accountList); $first = session('first')->format('Y-m-d'); $today = Carbon::create()->format('Y-m-d'); - $subTitle = (string)trans('firefly.execute_on_existing_transactions'); + $subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]); return view('rules.rule-group.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'ruleGroup', 'subTitle')); } @@ -246,14 +246,14 @@ class RuleGroupController extends Controller * @param RuleGroupRepositoryInterface $repository * @param RuleGroup $ruleGroup * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function update(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup) { $data = [ 'title' => $request->input('title'), 'description' => $request->input('description'), - 'active' => intval($request->input('active')) == 1, + 'active' => intval($request->input('active')) === 1, ]; $repository->update($ruleGroup, $data); diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index f014f8325a..615947e108 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers; use FireflyIII\Support\Search\SearchInterface; use Illuminate\Http\Request; -use Illuminate\Support\Collection; +use Response; use View; /** @@ -51,47 +51,29 @@ class SearchController extends Controller */ public function index(Request $request, SearchInterface $searcher) { - // yes, hard coded values: - $minSearchLen = 1; - $limit = 20; + $fullQuery = $request->get('q'); - // ui stuff: - $subTitle = ''; + // parse search terms: + $searcher->parseQuery($fullQuery); + $query = $searcher->getWordsAsString(); + $subTitle = trans('breadcrumbs.search_result', ['query' => $query]); - // query stuff - $query = null; - $result = []; - $rawQuery = $request->get('q'); - $hasModifiers = true; - $modifiers = []; + return view('search.index', compact('query', 'fullQuery', 'subTitle')); + } - if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) { - // parse query, find modifiers: - // set limit for search - $searcher->setLimit($limit); - $searcher->parseQuery($request->get('q')); + public function search(Request $request, SearchInterface $searcher) + { + $fullQuery = $request->get('query'); - $transactions = $searcher->searchTransactions(); - $accounts = new Collection; - $categories = new Collection; - $tags = new Collection; - $budgets = new Collection; + // parse search terms: + $searcher->parseQuery($fullQuery); + $searcher->setLimit(20); + $transactions = $searcher->searchTransactions(); + $html = view('search.search', compact('transactions'))->render(); - // no special search thing? - if (!$searcher->hasModifiers()) { - $hasModifiers = false; - $accounts = $searcher->searchAccounts(); - $categories = $searcher->searchCategories(); - $budgets = $searcher->searchBudgets(); - $tags = $searcher->searchTags(); - } - $query = $searcher->getWordsAsString(); - $subTitle = trans('firefly.search_results_for', ['query' => $query]); - $result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags]; + return Response::json(['count' => $transactions->count(), 'html' => $html]); - } - return view('search.index', compact('rawQuery', 'hasModifiers', 'modifiers', 'subTitle', 'limit', 'query', 'result')); } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index ce6f2db7d6..f373fa7dff 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -236,7 +236,7 @@ class TagController extends Controller // default values: $subTitle = $tag->tag; $subTitleIcon = 'fa-tag'; - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $count = 0; $loop = 0; @@ -301,7 +301,7 @@ class TagController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.journals_in_period_for_tag', ['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 8b3abddfce..65fdb571e0 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -86,7 +86,7 @@ class MassController extends Controller foreach ($ids as $journalId) { /** @var TransactionJournal $journal */ $journal = $repository->find(intval($journalId)); - if (!is_null($journal->id) && $journalId == $journal->id) { + if (!is_null($journal->id) && $journalId === $journal->id) { $set->push($journal); } } @@ -135,8 +135,8 @@ class MassController extends Controller * @var TransactionJournal $journal */ foreach ($journals as $index => $journal) { - $sources = $journal->sourceAccountList($journal); - $destinations = $journal->destinationAccountList($journal); + $sources = $journal->sourceAccountList(); + $destinations = $journal->destinationAccountList(); if ($sources->count() > 1) { $messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]); continue; @@ -213,11 +213,11 @@ class MassController extends Controller if ($journal) { // get optional fields: $what = strtolower($journal->transactionTypeStr()); - $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; + $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; $sourceAccountName = $request->get('source_account_name')[$journal->id] ?? ''; - $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; + $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; $destAccountName = $request->get('destination_account_name')[$journal->id] ?? ''; - $budgetId = $request->get('budget_id')[$journal->id] ?? 0; + $budgetId = $request->get('budget_id')[$journal->id] ?? 0; $category = $request->get('category')[$journal->id]; $tags = $journal->tags->pluck('tag')->toArray(); $amount = round($request->get('amount')[$journal->id], 12); diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index f091cc2691..b3e23c25a8 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -174,7 +174,7 @@ class SingleController extends Controller * * @param TransactionJournal $journal * - * @return View + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ public function delete(TransactionJournal $journal) { diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 6d088942b4..6919679807 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -70,7 +70,7 @@ class TransactionController extends Controller // default values: $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $count = 0; $loop = 0; @@ -93,6 +93,7 @@ class TransactionController extends Controller if (strlen($moment) > 0 && $moment !== 'all') { $start = new Carbon($moment); $end = Navigation::endOfPeriod($start, $range); + $path = '/transactions/' . $what . '/' . $moment; $subTitle = trans( 'firefly.title_' . $what . '_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] @@ -130,7 +131,7 @@ class TransactionController extends Controller } } - if ($moment != 'all' && $loop > 1) { + if ($moment !== 'all' && $loop > 1) { $subTitle = trans( 'firefly.title_' . $what . '_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] @@ -172,7 +173,7 @@ class TransactionController extends Controller * @param TransactionJournal $journal * @param JournalTaskerInterface $tasker * - * @return View + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ public function show(TransactionJournal $journal, JournalTaskerInterface $tasker) { diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index d61e5cb8d9..1348a0d767 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -37,7 +37,7 @@ class TagFormRequest extends Request */ public function collectTagData(): array { - if ($this->get('setTag') == 'true') { + if ($this->get('setTag') === 'true') { $latitude = $this->string('latitude'); $longitude = $this->string('longitude'); $zoomLevel = $this->integer('zoomLevel'); diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index aec8c16581..a9db6ca7c4 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -10,6 +10,7 @@ */ declare(strict_types=1); + use Carbon\Carbon; use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator; use FireflyIII\Exceptions\FireflyException; @@ -680,7 +681,7 @@ Breadcrumbs::register( Breadcrumbs::register( 'search.index', function (BreadCrumbGenerator $breadcrumbs, $query) { $breadcrumbs->parent('home'); - $breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search.index')); + $breadcrumbs->push(trans('breadcrumbs.search_result', ['query' => e($query)]), route('search.index')); } ); diff --git a/app/Import/Converter/Amount.php b/app/Import/Converter/Amount.php index 4cd861e25d..75f90a534d 100644 --- a/app/Import/Converter/Amount.php +++ b/app/Import/Converter/Amount.php @@ -36,10 +36,10 @@ class Amount implements ConverterInterface $decimalPosition = $len - 3; $decimal = null; - if (($len > 2 && $value{$decimalPosition} == '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) { + if (($len > 2 && $value{$decimalPosition} === '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) { $decimal = '.'; } - if ($len > 2 && $value{$decimalPosition} == ',') { + if ($len > 2 && $value{$decimalPosition} === ',') { $decimal = ','; } diff --git a/app/Import/Logging/CommandHandler.php b/app/Import/Logging/CommandHandler.php index b87830469d..ef06bbd517 100644 --- a/app/Import/Logging/CommandHandler.php +++ b/app/Import/Logging/CommandHandler.php @@ -36,7 +36,8 @@ class CommandHandler extends AbstractProcessingHandler { parent::__construct(); $this->command = $command; - $this->changeLevel(env('LOG_LEVEL', 'debug')); + + $this->changeLevel(env('APP_LOG_LEVEL', 'info')); } /** @@ -56,9 +57,10 @@ class CommandHandler extends AbstractProcessingHandler */ private function changeLevel(string $level) { - $level = strtoupper($level); - if (defined(sprintf('Logger::%s', $level))) { - $this->setLevel(constant(sprintf('Logger::%s', $level))); + $level = strtoupper($level); + $reference = sprintf('\Monolog\Logger::%s', $level); + if (defined($reference)) { + $this->setLevel(constant($reference)); } } } diff --git a/app/Import/Mapper/AssetAccountIbans.php b/app/Import/Mapper/AssetAccountIbans.php index 9f6d58ac5b..9d53bf6d8c 100644 --- a/app/Import/Mapper/AssetAccountIbans.php +++ b/app/Import/Mapper/AssetAccountIbans.php @@ -42,7 +42,7 @@ class AssetAccountIbans implements MapperInterface if (strlen($iban) > 0) { $topList[$account->id] = $account->iban . ' (' . $account->name . ')'; } - if (strlen($iban) == 0) { + if (strlen($iban) === 0) { $list[$account->id] = $account->name; } } diff --git a/app/Import/Mapper/OpposingAccountIbans.php b/app/Import/Mapper/OpposingAccountIbans.php index 3a4299c26a..121395cac4 100644 --- a/app/Import/Mapper/OpposingAccountIbans.php +++ b/app/Import/Mapper/OpposingAccountIbans.php @@ -48,7 +48,7 @@ class OpposingAccountIbans implements MapperInterface if (strlen($iban) > 0) { $topList[$account->id] = $account->iban . ' (' . $account->name . ')'; } - if (strlen($iban) == 0) { + if (strlen($iban) === 0) { $list[$account->id] = $account->name; } } diff --git a/app/Import/Object/ImportAccount.php b/app/Import/Object/ImportAccount.php index 334709d8bb..ca5dba85d5 100644 --- a/app/Import/Object/ImportAccount.php +++ b/app/Import/Object/ImportAccount.php @@ -37,6 +37,8 @@ class ImportAccount private $accountName = []; /** @var array */ private $accountNumber = []; + /** @var int */ + private $defaultAccountId = 0; /** @var string */ private $expectedType = ''; /** @var AccountRepositoryInterface */ @@ -115,6 +117,14 @@ class ImportAccount $this->accountNumber = $accountNumber; } + /** + * @param int $defaultAccountId + */ + public function setDefaultAccountId(int $defaultAccountId) + { + $this->defaultAccountId = $defaultAccountId; + } + /** * @param User $user */ @@ -249,13 +259,22 @@ class ImportAccount $search = intval($array['mapped']); $account = $this->repository->find($search); - if ($account->accountType->type !== $this->expectedType) { + if (is_null($account->id)) { + Log::error(sprintf('There is no account with id #%d. Invalid mapping will be ignored!', $search)); + + return new Account; + } + // must be of the same type + // except when mapped is an asset, then it's fair game. + // which only shows that user must map very carefully. + if ($account->accountType->type !== $this->expectedType && $account->accountType->type !== AccountType::ASSET) { Log::error( sprintf( 'Mapped account #%d is of type "%s" but we expect a "%s"-account. Mapping will be ignored.', $account->id, $account->accountType->type, $this->expectedType ) ); + return new Account; } @@ -297,6 +316,14 @@ class ImportAccount } $this->expectedType = $oldExpectedType; + // if search for an asset account, fall back to given "default account" (mandatory) + if ($this->expectedType === AccountType::ASSET) { + $this->account = $this->repository->find($this->defaultAccountId); + Log::debug(sprintf('Fall back to default account #%d "%s"', $this->account->id, $this->account->name)); + + return true; + } + Log::debug(sprintf('Found no account of type %s so must create one ourselves.', $this->expectedType)); $data = [ diff --git a/app/Import/Object/ImportBill.php b/app/Import/Object/ImportBill.php index 729a290d3b..1760b33126 100644 --- a/app/Import/Object/ImportBill.php +++ b/app/Import/Object/ImportBill.php @@ -180,12 +180,19 @@ class ImportBill Log::debug('Finding a mapped bill based on', $array); - $search = intval($array['mapped']); - $account = $this->repository->find($search); + $search = intval($array['mapped']); + $bill = $this->repository->find($search); - Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $account->id, $account->name)); + if (is_null($bill->id)) { + Log::error(sprintf('There is no bill with id #%d. Invalid mapping will be ignored!', $search)); - return $account; + return new Bill; + } + + + Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $bill->id, $bill->name)); + + return $bill; } /** diff --git a/app/Import/Object/ImportBudget.php b/app/Import/Object/ImportBudget.php index 011fef8c8e..07124ec0f2 100644 --- a/app/Import/Object/ImportBudget.php +++ b/app/Import/Object/ImportBudget.php @@ -180,12 +180,18 @@ class ImportBudget Log::debug('Finding a mapped budget based on', $array); - $search = intval($array['mapped']); - $account = $this->repository->find($search); + $search = intval($array['mapped']); + $budget = $this->repository->find($search); - Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $account->id, $account->name)); + if (is_null($budget->id)) { + Log::error(sprintf('There is no budget with id #%d. Invalid mapping will be ignored!', $search)); - return $account; + return new Budget; + } + + Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $budget->id, $budget->name)); + + return $budget; } /** diff --git a/app/Import/Object/ImportCategory.php b/app/Import/Object/ImportCategory.php index 69265fcd3a..f9e56425d1 100644 --- a/app/Import/Object/ImportCategory.php +++ b/app/Import/Object/ImportCategory.php @@ -174,12 +174,18 @@ class ImportCategory Log::debug('Finding a mapped category based on', $array); - $search = intval($array['mapped']); - $account = $this->repository->find($search); + $search = intval($array['mapped']); + $category = $this->repository->find($search); - Log::debug(sprintf('Found category! #%d ("%s"). Return it', $account->id, $account->name)); + if (is_null($category->id)) { + Log::error(sprintf('There is no category with id #%d. Invalid mapping will be ignored!', $search)); - return $account; + return new Category; + } + + Log::debug(sprintf('Found category! #%d ("%s"). Return it', $category->id, $category->name)); + + return $category; } /** diff --git a/app/Import/Object/ImportCurrency.php b/app/Import/Object/ImportCurrency.php index 983c2591ae..e282e29228 100644 --- a/app/Import/Object/ImportCurrency.php +++ b/app/Import/Object/ImportCurrency.php @@ -209,6 +209,13 @@ class ImportCurrency $search = intval($array['mapped']); $currency = $this->repository->find($search); + + if (is_null($currency->id)) { + Log::error(sprintf('There is no currency with id #%d. Invalid mapping will be ignored!', $search)); + + return new TransactionCurrency; + } + Log::debug(sprintf('Found currency! #%d ("%s"). Return it', $currency->id, $currency->name)); return $currency; diff --git a/app/Import/Object/ImportJournal.php b/app/Import/Object/ImportJournal.php index adebaea1f6..3a0b74ade5 100644 --- a/app/Import/Object/ImportJournal.php +++ b/app/Import/Object/ImportJournal.php @@ -144,6 +144,18 @@ class ImportJournal return $date; } + /** + * @return string + */ + public function getDescription(): string + { + if ($this->description === '') { + return '(no description)'; + } + + return $this->description; + } + /** * @param string $hash */ @@ -226,7 +238,7 @@ class ImportJournal $this->date = $array['value']; break; case 'description': - $this->description = $array['value']; + $this->description .= $array['value']; break; case 'sepa-ct-op': case 'sepa-ct-id': diff --git a/app/Import/Routine/ImportRoutine.php b/app/Import/Routine/ImportRoutine.php index ee8fd22c6b..b032bc06d8 100644 --- a/app/Import/Routine/ImportRoutine.php +++ b/app/Import/Routine/ImportRoutine.php @@ -13,11 +13,11 @@ namespace FireflyIII\Import\Routine; use Carbon\Carbon; +use DB; use FireflyIII\Import\FileProcessor\FileProcessorInterface; use FireflyIII\Import\Storage\ImportStorage; use FireflyIII\Models\ImportJob; use FireflyIII\Models\Tag; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Log; @@ -64,14 +64,19 @@ class ImportRoutine Log::info(sprintf('Returned %d valid objects from file processor', $this->lines)); $storage = $this->storeObjects($importObjects); + Log::debug('Back in run()'); // update job: $this->job->status = 'finished'; $this->job->save(); + Log::debug('Updated job...'); + $this->journals = $storage->journals; $this->errors = $storage->errors; + Log::debug('Going to call createImportTag()'); + // create tag, link tag to all journals: $this->createImportTag(); @@ -101,7 +106,7 @@ class ImportRoutine $processor = app($class); $processor->setJob($this->job); - if ($this->job->status == 'configured') { + if ($this->job->status === 'configured') { // set job as "running"... $this->job->status = 'running'; @@ -120,6 +125,7 @@ class ImportRoutine */ private function createImportTag(): Tag { + Log::debug('Now in createImportTag()'); /** @var TagRepositoryInterface $repository */ $repository = app(TagRepositoryInterface::class); $repository->setUser($this->job->user); @@ -138,14 +144,17 @@ class ImportRoutine $this->job->extended_status = $extended; $this->job->save(); - $this->journals->each( - function (TransactionJournal $journal) use ($tag) { - $journal->tags()->save($tag); - } - ); + Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag)); + Log::debug('Looping journals...'); + $journalIds = $this->journals->pluck('id')->toArray(); + $tagId = $tag->id; + foreach ($journalIds as $journalId) { + Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId)); + DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]); + } + Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag)); return $tag; - } /** @@ -160,6 +169,7 @@ class ImportRoutine $storage->setDateFormat($this->job->configuration['date-format']); $storage->setObjects($objects); $storage->store(); + Log::info('Back in storeObjects()'); return $storage; } diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php index b0eebd0904..6ae80774b5 100644 --- a/app/Import/Specifics/AbnAmroDescription.php +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -103,7 +103,7 @@ class AbnAmroDescription implements SpecificInterface $this->row[8] = $matches[4]; // 'opposing-account-name' $this->row[7] = $matches[4]; // 'description' - if ($matches[1] == 'GEA') { + if ($matches[1] === 'GEA') { $this->row[7] = 'GEA ' . $matches[4]; // 'description' } diff --git a/app/Import/Storage/ImportStorage.php b/app/Import/Storage/ImportStorage.php index 4986bfbb29..ad0904b866 100644 --- a/app/Import/Storage/ImportStorage.php +++ b/app/Import/Storage/ImportStorage.php @@ -29,6 +29,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Rules\Processor; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; use Steam; @@ -43,6 +44,7 @@ class ImportStorage { /** @var Collection */ public $errors; + /** @var Collection */ public $journals; /** @var CurrencyRepositoryInterface */ private $currencyRepository; @@ -96,13 +98,12 @@ class ImportStorage } /** - * Do storage of import objects + * Do storage of import objects. */ public function store() { $this->defaultCurrency = Amount::getDefaultCurrencyByUser($this->job->user); - // routine below consists of 3 steps. /** * @var int $index * @var ImportJournal $object @@ -115,8 +116,9 @@ class ImportStorage Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage())); } } + Log::info('ImportStorage has finished.'); - + return true; } /** @@ -127,7 +129,6 @@ class ImportStorage protected function applyRules(TransactionJournal $journal): bool { if ($this->rules->count() > 0) { - /** @var Rule $rule */ foreach ($this->rules as $rule) { Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id)); @@ -164,14 +165,60 @@ class ImportStorage $errorText = join(', ', $transaction->getErrors()->all()); throw new FireflyException($errorText); } - Log::debug(sprintf('Created transaction with ID #%d and account #%d', $transaction->id, $accountId)); + Log::debug(sprintf('Created transaction with ID #%d, account #%d, amount %s', $transaction->id, $accountId, $amount)); return true; } /** + * @param Collection $set * @param ImportJournal $importJournal * + * @return bool + */ + private function filterTransferSet(Collection $set, ImportJournal $importJournal): bool + { + $amount = Steam::positive($importJournal->getAmount()); + $asset = $importJournal->asset->getAccount(); + $opposing = $this->getOpposingAccount($importJournal->opposing, $amount); + $description = $importJournal->getDescription(); + + $filtered = $set->filter( + function (TransactionJournal $journal) use ($asset, $opposing, $description) { + $match = true; + $original = [app('steam')->tryDecrypt($journal->source_name), app('steam')->tryDecrypt($journal->destination_name)]; + $compare = [$asset->name, $opposing->name]; + sort($original); + sort($compare); + + // description does not match? Then cannot be duplicate. + if ($journal->description !== $description) { + $match = false; + } + // not both accounts in journal? Then cannot be duplicate. + if ($original !== $compare) { + $match = false; + } + + if ($match) { + return $journal; + } + + return null; + } + ); + if (count($filtered) > 0) { + return true; + } + + return false; + } + + /** + * @param ImportJournal $importJournal + * + * @param Account $account + * * @return TransactionCurrency */ private function getCurrency(ImportJournal $importJournal, Account $account): TransactionCurrency @@ -300,8 +347,8 @@ class ImportStorage private function storeImportJournal(int $index, ImportJournal $importJournal): bool { - Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->description)); - + Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->getDescription())); + $importJournal->asset->setDefaultAccountId($this->job->configuration['import-account']); $asset = $importJournal->asset->getAccount(); $amount = $importJournal->getAmount(); $currency = $this->getCurrency($importJournal, $asset); @@ -317,18 +364,30 @@ class ImportStorage // verify that opposing account is of the correct type: if ($opposing->accountType->type === AccountType::EXPENSE && $transactionType->type !== TransactionType::WITHDRAWAL) { - Log::error(sprintf('Row #%d is imported as a %s but opposing is an expense account. This cannot be!', $index, $transactionType->type)); + $message = sprintf('Row #%d is imported as a %s but opposing is an expense account. This cannot be!', $index, $transactionType->type); + Log::error($message); + throw new FireflyException($message); + } /*** First step done! */ $this->job->addStepsDone(1); + // could be that transfer is double: verify this. + if ($this->verifyDoubleTransfer($transactionType, $importJournal)) { + // add three steps: + $this->job->addStepsDone(3); + // throw error + throw new FireflyException('Detected a possible duplicate, skip this one.'); + + } + // create a journal: $journal = new TransactionJournal; $journal->user_id = $this->job->user_id; $journal->transaction_type_id = $transactionType->id; $journal->transaction_currency_id = $currency->id; - $journal->description = $importJournal->description; + $journal->description = $importJournal->getDescription(); $journal->date = $date->format('Y-m-d'); $journal->order = 0; $journal->tag_count = 0; @@ -336,7 +395,9 @@ class ImportStorage if (!$journal->save()) { $errorText = join(', ', $journal->getErrors()->all()); + // add three steps: $this->job->addStepsDone(3); + // throw error throw new FireflyException($errorText); } @@ -371,7 +432,6 @@ class ImportStorage // run rules: $this->applyRules($journal); $this->job->addStepsDone(1); - $this->journals->push($journal); Log::info( @@ -402,4 +462,49 @@ class ImportStorage } } + /** + * This method checks if the given transaction is a transfer and if so, if it might be a duplicate of an already imported transfer. + * This is important for import files that cover multiple accounts (and include both A<>B and B<>A transactions). + * + * @param TransactionType $transactionType + * @param ImportJournal $importJournal + * + * @return bool + */ + private function verifyDoubleTransfer(TransactionType $transactionType, ImportJournal $importJournal): bool + { + if ($transactionType->type === TransactionType::TRANSFER) { + $amount = Steam::positive($importJournal->getAmount()); + $date = $importJournal->getDate($this->dateFormat); + $set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions AS source', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'source.transaction_journal_id')->where('source.amount', '<', 0); + } + ) + ->leftJoin( + 'transactions AS destination', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'destination.transaction_journal_id')->where( + 'destination.amount', '>', 0 + ); + } + ) + ->leftJoin('accounts as source_accounts', 'source.account_id', '=', 'source_accounts.id') + ->leftJoin('accounts as destination_accounts', 'destination.account_id', '=', 'destination_accounts.id') + ->where('transaction_journals.user_id', $this->job->user_id) + ->where('transaction_types.type', TransactionType::TRANSFER) + ->where('transaction_journals.date', $date->format('Y-m-d')) + ->where('destination.amount', $amount) + ->get( + ['transaction_journals.id', 'transaction_journals.encrypted', 'transaction_journals.description', + 'source_accounts.name as source_name', 'destination_accounts.name as destination_name'] + ); + + return $this->filterTransferSet($set, $importJournal); + } + + + return false; + } + } diff --git a/app/Jobs/ExecuteRuleOnExistingTransactions.php b/app/Jobs/ExecuteRuleOnExistingTransactions.php new file mode 100644 index 0000000000..b8f9c2bb6a --- /dev/null +++ b/app/Jobs/ExecuteRuleOnExistingTransactions.php @@ -0,0 +1,161 @@ +rule = $rule; + } + + /** + * @return Collection + */ + public function getAccounts(): Collection + { + return $this->accounts; + } + + /** + * + * @param Collection $accounts + */ + public function setAccounts(Collection $accounts) + { + $this->accounts = $accounts; + } + + /** + * @return \Carbon\Carbon + */ + public function getEndDate(): Carbon + { + return $this->endDate; + } + + /** + * + * @param Carbon $date + */ + public function setEndDate(Carbon $date) + { + $this->endDate = $date; + } + + /** + * @return \Carbon\Carbon + */ + public function getStartDate(): Carbon + { + return $this->startDate; + } + + /** + * + * @param Carbon $date + */ + public function setStartDate(Carbon $date) + { + $this->startDate = $date; + } + + /** + * @return User + */ + public function getUser(): User + { + return $this->user; + } + + /** + * + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + // Lookup all journals that match the parameters specified + $transactions = $this->collectJournals(); + $processor = Processor::make($this->rule); + + // Execute the rules for each transaction + foreach ($transactions as $transaction) { + + $processor->handleTransaction($transaction); + + // Stop processing this group if the rule specifies 'stop_processing' + if ($processor->getRule()->stop_processing) { + break; + } + } + + } + + /** + * Collect all journals that should be processed + * + * @return Collection + */ + protected function collectJournals() + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); + $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); + + return $collector->getJournals(); + } + +} diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index 05de2aca0a..fd4a0164ff 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -80,7 +80,7 @@ class MailError extends Job implements ShouldQueue Mail::send( ['emails.error-html', 'emails.error-text'], $args, function (Message $message) use ($email) { - if ($email != 'mail@example.com') { + if ($email !== 'mail@example.com') { $message->to($email, $email)->subject('Caught an error in Firely III'); } } diff --git a/app/Models/Account.php b/app/Models/Account.php index cfef7e34af..26ab5424a5 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -95,7 +95,7 @@ class Account extends Model /** @var Account $account */ foreach ($set as $account) { - if ($account->name == $fields['name']) { + if ($account->name === $fields['name']) { return $account; } } @@ -116,7 +116,7 @@ class Account extends Model { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } @@ -187,7 +187,7 @@ class Account extends Model public function getMeta(string $fieldName): string { foreach ($this->accountMeta as $meta) { - if ($meta->name == $fieldName) { + if ($meta->name === $fieldName) { return strval($meta->data); } } diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index ddca8ad0d1..bc9fd231f7 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -55,7 +55,7 @@ class Attachment extends Model { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index 4a858720e5..8659be7749 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -62,7 +62,7 @@ class Bill extends Model public static function routeBinder(Bill $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } @@ -77,7 +77,7 @@ class Bill extends Model public function getMatchAttribute($value) { - if (intval($this->match_encrypted) == 1) { + if (intval($this->match_encrypted) === 1) { return Crypt::decrypt($value); } @@ -92,7 +92,7 @@ class Bill extends Model public function getNameAttribute($value) { - if (intval($this->name_encrypted) == 1) { + if (intval($this->name_encrypted) === 1) { return Crypt::decrypt($value); } diff --git a/app/Models/Budget.php b/app/Models/Budget.php index d5f71f21ee..6f1a5f3386 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -66,7 +66,7 @@ class Budget extends Model $set = $query->get(['budgets.*']); /** @var Budget $budget */ foreach ($set as $budget) { - if ($budget->name == $fields['name']) { + if ($budget->name === $fields['name']) { return $budget; } } @@ -85,7 +85,7 @@ class Budget extends Model public static function routeBinder(Budget $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/Category.php b/app/Models/Category.php index fd7241702f..85894a271a 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -67,7 +67,7 @@ class Category extends Model $set = $query->get(['categories.*']); /** @var Category $category */ foreach ($set as $category) { - if ($category->name == $fields['name']) { + if ($category->name === $fields['name']) { return $category; } } @@ -86,7 +86,7 @@ class Category extends Model public static function routeBinder(Category $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 4f23de2c83..dea6e581c9 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -124,7 +124,7 @@ class ImportJob extends Model if (is_null($value)) { return []; } - if (strlen($value) == 0) { + if (strlen($value) === 0) { return []; } @@ -138,7 +138,7 @@ class ImportJob extends Model */ public function getExtendedStatusAttribute($value) { - if (strlen($value) == 0) { + if (strlen($value) === 0) { return []; } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index e648623ba4..2c8d67539c 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -58,7 +58,7 @@ class PiggyBank extends Model public static function routeBinder(PiggyBank $value) { if (auth()->check()) { - if ($value->account->user_id == auth()->user()->id) { + if (intval($value->account->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 28637d2893..e8f334167b 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -51,7 +51,7 @@ class Rule extends Model public static function routeBinder(Rule $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 8ab7f6a117..98ac15354b 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -52,7 +52,7 @@ class RuleGroup extends Model public static function routeBinder(RuleGroup $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 21430124c2..c7a67e4f6a 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -66,7 +66,7 @@ class Tag extends Model $set = $query->get(['tags.*']); /** @var Tag $tag */ foreach ($set as $tag) { - if ($tag->tag == $fields['tag']) { + if ($tag->tag === $fields['tag']) { return $tag; } } @@ -87,7 +87,7 @@ class Tag extends Model public static function routeBinder(Tag $value) { if (auth()->check()) { - if ($value->user_id == auth()->user()->id) { + if (intval($value->user_id) === auth()->user()->id) { return $value; } } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index bfb4de8bdc..977ef1ea90 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -207,7 +207,7 @@ class TransactionJournal extends Model public function isDeposit(): bool { if (!is_null($this->transaction_type_type)) { - return $this->transaction_type_type == TransactionType::DEPOSIT; + return $this->transaction_type_type === TransactionType::DEPOSIT; } return $this->transactionType->isDeposit(); @@ -220,7 +220,7 @@ class TransactionJournal extends Model public function isOpeningBalance(): bool { if (!is_null($this->transaction_type_type)) { - return $this->transaction_type_type == TransactionType::OPENING_BALANCE; + return $this->transaction_type_type === TransactionType::OPENING_BALANCE; } return $this->transactionType->isOpeningBalance(); @@ -233,7 +233,7 @@ class TransactionJournal extends Model public function isTransfer(): bool { if (!is_null($this->transaction_type_type)) { - return $this->transaction_type_type == TransactionType::TRANSFER; + return $this->transaction_type_type === TransactionType::TRANSFER; } return $this->transactionType->isTransfer(); @@ -246,7 +246,7 @@ class TransactionJournal extends Model public function isWithdrawal(): bool { if (!is_null($this->transaction_type_type)) { - return $this->transaction_type_type == TransactionType::WITHDRAWAL; + return $this->transaction_type_type === TransactionType::WITHDRAWAL; } return $this->transactionType->isWithdrawal(); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 86bd353c0a..b529d8c625 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -25,6 +25,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Log; +use Validator; /** @@ -178,7 +179,7 @@ class AccountRepository implements AccountRepositoryInterface { // update the account: $account->name = $data['name']; - $account->active = $data['active'] == '1' ? true : false; + $account->active = $data['active'] === '1' ? true : false; $account->virtual_balance = $data['virtualBalance']; $account->iban = $data['iban']; $account->save(); @@ -236,7 +237,7 @@ class AccountRepository implements AccountRepositoryInterface $data['accountType'] = $data['accountType'] ?? 'invalid'; $type = config('firefly.accountTypeByIdentifier.' . $data['accountType']); $accountType = AccountType::whereType($type)->first(); - + $data['iban'] = $this->filterIban($data['iban']); // verify account type if (is_null($accountType)) { throw new FireflyException(sprintf('Account type "%s" is invalid. Cannot create account.', $data['accountType'])); @@ -251,16 +252,17 @@ class AccountRepository implements AccountRepositoryInterface } // create it: - $newAccount = new Account( - [ - 'user_id' => $this->user->id, - 'account_type_id' => $accountType->id, - 'name' => $data['name'], - 'virtual_balance' => $data['virtualBalance'], - 'active' => $data['active'] === true ? true : false, - 'iban' => $data['iban'], - ] - ); + $databaseData + = [ + 'user_id' => $this->user->id, + 'account_type_id' => $accountType->id, + 'name' => $data['name'], + 'virtual_balance' => $data['virtualBalance'], + 'active' => $data['active'] === true ? true : false, + 'iban' => $data['iban'], + ]; + $newAccount = new Account($databaseData); + Log::debug('Final account creation dataset', $databaseData); $newAccount->save(); // verify its creation: if (is_null($newAccount->id)) { @@ -268,6 +270,7 @@ class AccountRepository implements AccountRepositoryInterface sprintf('Could not create account "%s" (%d error(s))', $data['name'], $newAccount->getErrors()->count()), $newAccount->getErrors()->toArray() ); throw new FireflyException(sprintf('Tried to create account named "%s" but failed. The logs have more details.', $data['name'])); + } Log::debug(sprintf('Created new account #%d named "%s" of type %s.', $newAccount->id, $newAccount->name, $accountType->type)); @@ -457,12 +460,12 @@ class AccountRepository implements AccountRepositoryInterface // update transactions: /** @var Transaction $transaction */ foreach ($journal->transactions()->get() as $transaction) { - if ($account->id == $transaction->account_id) { + if ($account->id === $transaction->account_id) { $transaction->amount = $amount; $transaction->transaction_currency_id = $currencyId; $transaction->save(); } - if ($account->id != $transaction->account_id) { + if ($account->id !== $transaction->account_id) { $transaction->amount = bcmul($amount, '-1'); $transaction->transaction_currency_id = $currencyId; $transaction->save(); @@ -491,4 +494,26 @@ class AccountRepository implements AccountRepositoryInterface return false; } + + /** + * @param null|string $iban + * + * @return null|string + */ + private function filterIban(string $iban = null) + { + if (is_null($iban)) { + return null; + } + $data = ['iban' => $iban]; + $rules = ['iban' => 'required|iban']; + $validator = Validator::make($data, $rules); + if ($validator->fails()) { + Log::error(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban)); + + return null; + } + + return $iban; + } } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 00ce78394f..e6957abe4b 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -116,7 +116,7 @@ class BillRepository implements BillRepositoryInterface $set = $set->sortBy( function (Bill $bill) { - $int = $bill->active == 1 ? 0 : 1; + $int = $bill->active === 1 ? 0 : 1; return $int . strtolower($bill->name); } @@ -168,7 +168,7 @@ class BillRepository implements BillRepositoryInterface $set = $set->sortBy( function (Bill $bill) { - $int = $bill->active == 1 ? 0 : 1; + $int = $bill->active === 1 ? 0 : 1; return $int . strtolower($bill->name); } @@ -500,7 +500,7 @@ class BillRepository implements BillRepositoryInterface return true; } - if ($bill->id == $journal->bill_id) { + if ($bill->id === $journal->bill_id) { // if no match, but bill used to match, remove it: $journal->bill_id = null; $journal->save(); diff --git a/app/Repositories/Journal/CreateJournalsTrait.php b/app/Repositories/Journal/CreateJournalsTrait.php index 44064b7432..13edb07aa7 100644 --- a/app/Repositories/Journal/CreateJournalsTrait.php +++ b/app/Repositories/Journal/CreateJournalsTrait.php @@ -32,7 +32,14 @@ use Log; */ trait CreateJournalsTrait { - + /** + * @param User $user + * @param TransactionType $type + * @param array $data + * + * @return array + */ + abstract public function storeAccounts(User $user, TransactionType $type, array $data): array; /** * diff --git a/app/Repositories/Journal/UpdateJournalsTrait.php b/app/Repositories/Journal/UpdateJournalsTrait.php index 9bf2cae058..d4264818a6 100644 --- a/app/Repositories/Journal/UpdateJournalsTrait.php +++ b/app/Repositories/Journal/UpdateJournalsTrait.php @@ -73,7 +73,7 @@ trait UpdateJournalsTrait protected function updateDestinationTransaction(TransactionJournal $journal, Account $account, array $data) { $set = $journal->transactions()->where('amount', '>', 0)->get(); - if ($set->count() != 1) { + if ($set->count() !== 1) { throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count())); } /** @var Transaction $transaction */ @@ -98,7 +98,7 @@ trait UpdateJournalsTrait { // should be one: $set = $journal->transactions()->where('amount', '<', 0)->get(); - if ($set->count() != 1) { + if ($set->count() !== 1) { throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count())); } /** @var Transaction $transaction */ @@ -140,7 +140,7 @@ trait UpdateJournalsTrait DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete(); } // if count is zero, delete them all: - if (count($ids) == 0) { + if (count($ids) === 0) { DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete(); } diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 47a848427c..8d40076c71 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -236,7 +236,7 @@ class RuleRepository implements RuleRepositoryInterface $rule->rule_group_id = $data['rule_group_id']; $rule->order = ($order + 1); $rule->active = 1; - $rule->stop_processing = intval($data['stop_processing']) == 1; + $rule->stop_processing = intval($data['stop_processing']) === 1; $rule->title = $data['title']; $rule->description = strlen($data['description']) > 0 ? $data['description'] : null; @@ -265,7 +265,7 @@ class RuleRepository implements RuleRepositoryInterface $ruleAction->active = 1; $ruleAction->stop_processing = $values['stopProcessing']; $ruleAction->action_type = $values['action']; - $ruleAction->action_value = $values['value']; + $ruleAction->action_value = is_null($values['value']) ? '' : $values['value']; $ruleAction->save(); diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index ab1ab14317..581fb72ebf 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -282,7 +282,7 @@ class TagRepository implements TagRepositoryInterface * changed to an advancePayment. */ - if ($tag->tagMode == 'balancingAct' || $tag->tagMode == 'nothing') { + if ($tag->tagMode === 'balancingAct' || $tag->tagMode === 'nothing') { foreach ($tag->transactionjournals as $journal) { if ($journal->isTransfer()) { return false; @@ -394,7 +394,7 @@ class TagRepository implements TagRepositoryInterface } // if already has transaction journals, must match ALL asset account id's: - if ($deposits > 0 || $withdrawals == 1) { + if ($deposits > 0 || $withdrawals === 1) { Log::debug('Need to match all asset accounts.'); return $this->matchAll($journal, $tag); diff --git a/app/Rules/Actions/RemoveTag.php b/app/Rules/Actions/RemoveTag.php index ca9912f5f0..525579f0ed 100644 --- a/app/Rules/Actions/RemoveTag.php +++ b/app/Rules/Actions/RemoveTag.php @@ -52,7 +52,7 @@ class RemoveTag implements ActionInterface /** @var Tag $tag */ $tag = $journal->user->tags()->get()->filter( function (Tag $tag) use ($name) { - return $tag->tag == $name; + return $tag->tag === $name; } )->first(); diff --git a/app/Rules/Actions/SetBudget.php b/app/Rules/Actions/SetBudget.php index 62b530b8c8..599f0214da 100644 --- a/app/Rules/Actions/SetBudget.php +++ b/app/Rules/Actions/SetBudget.php @@ -56,7 +56,7 @@ class SetBudget implements ActionInterface $budgets = $repository->getActiveBudgets(); $budget = $budgets->filter( function (Budget $current) use ($search) { - return $current->name == $search; + return $current->name === $search; } )->first(); if (is_null($budget)) { @@ -65,7 +65,7 @@ class SetBudget implements ActionInterface return true; } - if ($journal->transactionType->type == TransactionType::TRANSFER) { + if ($journal->transactionType->type === TransactionType::TRANSFER) { Log::debug(sprintf('RuleAction SetBudget could not set budget of journal #%d to "%s" because journal is a transfer.', $journal->id, $search)); return true; diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index 579521af61..478c7bf0df 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -59,9 +59,11 @@ final class Processor * * @param Rule $rule * + * @param bool $includeActions + * * @return Processor */ - public static function make(Rule $rule) + public static function make(Rule $rule, $includeActions = true) { Log::debug(sprintf('Making new rule from Rule %d', $rule->id)); $self = new self; @@ -72,7 +74,9 @@ final class Processor Log::debug(sprintf('Push trigger %d', $trigger->id)); $self->triggers->push(TriggerFactory::getTrigger($trigger)); } - $self->actions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); + if ($includeActions) { + $self->actions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); + } return $self; } @@ -254,7 +258,7 @@ final class Processor } } - $result = ($hitTriggers == $foundTriggers && $foundTriggers > 0); + $result = ($hitTriggers === $foundTriggers && $foundTriggers > 0); Log::debug('Result of triggered()', ['hitTriggers' => $hitTriggers, 'foundTriggers' => $foundTriggers, 'result' => $result]); return $result; diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index 090d3553ba..b192f64d7d 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace FireflyIII\Rules; use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Models\Rule; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalTaskerInterface; @@ -32,6 +33,8 @@ class TransactionMatcher private $limit = 10; /** @var int Maximum number of transaction to search in (for performance reasons) * */ private $range = 200; + /** @var Rule */ + private $rule; /** @var JournalTaskerInterface */ private $tasker; /** @var array */ @@ -50,6 +53,31 @@ class TransactionMatcher } + /** + * This method will search the user's transaction journal (with an upper limit of $range) for + * transaction journals matching the given rule. This is accomplished by trying to fire these + * triggers onto each transaction journal until enough matches are found ($limit). + * + * @return Collection + * + */ + public function findTransactionsByRule() + { + if (count($this->rule->ruleTriggers) === 0) { + return new Collection; + } + + // Variables used within the loop + $processor = Processor::make($this->rule, false); + $result = $this->runProcessor($processor); + + // If the list of matchingTransactions is larger than the maximum number of results + // (e.g. if a large percentage of the transactions match), truncate the list + $result = $result->slice(0, $this->limit); + + return $result; + } + /** * This method will search the user's transaction journal (with an upper limit of $range) for * transaction journals matching the given $triggers. This is accomplished by trying to fire these @@ -58,64 +86,15 @@ class TransactionMatcher * @return Collection * */ - public function findMatchingTransactions(): Collection + public function findTransactionsByTriggers(): Collection { if (count($this->triggers) === 0) { return new Collection; } - $pageSize = min($this->range / 2, $this->limit * 2); // Variables used within the loop - $processed = 0; - $page = 1; - $result = new Collection(); $processor = Processor::makeFromStringArray($this->triggers); - - // Start a loop to fetch batches of transactions. The loop will finish if: - // - all transactions have been fetched from the database - // - the maximum number of transactions to return has been found - // - the maximum number of transactions to search in have been searched - do { - // Fetch a batch of transactions from the database - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); - $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes); - $set = $collector->getPaginatedJournals(); - Log::debug(sprintf('Found %d journals to check. ', $set->count())); - - // Filter transactions that match the given triggers. - $filtered = $set->filter( - function (Transaction $transaction) use ($processor) { - Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); - - return $processor->handleTransaction($transaction); - } - ); - - Log::debug(sprintf('Found %d journals that match.', $filtered->count())); - - // merge: - /** @var Collection $result */ - $result = $result->merge($filtered); - Log::debug(sprintf('Total count is now %d', $result->count())); - - // Update counters - $page++; - $processed += count($set); - - Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); - - // Check for conditions to finish the loop - $reachedEndOfList = $set->count() < 1; - $foundEnough = $result->count() >= $this->limit; - $searchedEnough = ($processed >= $this->range); - - Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); - Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); - Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true))); - - } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); + $result = $this->runProcessor($processor); // If the list of matchingTransactions is larger than the maximum number of results // (e.g. if a large percentage of the transactions match), truncate the list @@ -185,5 +164,73 @@ class TransactionMatcher return $this; } + /** + * @param Rule $rule + */ + public function setRule(Rule $rule) + { + $this->rule = $rule; + } + + /** + * @param Processor $processor + * + * @return Collection + */ + private function runProcessor(Processor $processor): Collection + { + // Start a loop to fetch batches of transactions. The loop will finish if: + // - all transactions have been fetched from the database + // - the maximum number of transactions to return has been found + // - the maximum number of transactions to search in have been searched + $pageSize = min($this->range / 2, $this->limit * 2); + $processed = 0; + $page = 1; + $result = new Collection(); + do { + // Fetch a batch of transactions from the database + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); + + // Filter transactions that match the given triggers. + $filtered = $set->filter( + function (Transaction $transaction) use ($processor) { + Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); + + return $processor->handleTransaction($transaction); + } + ); + + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + + // merge: + /** @var Collection $result */ + $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); + + // Update counters + $page++; + $processed += count($set); + + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + + // Check for conditions to finish the loop + $reachedEndOfList = $set->count() < 1; + $foundEnough = $result->count() >= $this->limit; + $searchedEnough = ($processed >= $this->range); + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true))); + + } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); + + return $result; + } + } diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 6d68b4b455..5662f8261b 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -311,6 +311,9 @@ class Amount $coloured = false; $format = '%s'; } + if ($transaction->transaction_type_type === TransactionType::OPENING_BALANCE) { + $amount = strval($transaction->transaction_amount); + } $currency = new TransactionCurrency; $currency->symbol = $transaction->transaction_currency_symbol; diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 555fe427b0..f9ffe8da7c 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -18,7 +18,6 @@ use Cache; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Support\Collection; -use Log; use Preferences as Prefs; /** @@ -75,7 +74,7 @@ class CacheProperties */ public function has(): bool { - if (getenv('APP_ENV') == 'testing') { + if (getenv('APP_ENV') === 'testing') { return false; } $this->md5(); @@ -119,8 +118,6 @@ class CacheProperties $this->md5 .= json_encode($property); } - Log::debug(sprintf('Cache string is %s', $this->md5)); $this->md5 = md5($this->md5); - Log::debug(sprintf('Cache MD5 is %s', $this->md5)); } } diff --git a/app/Support/Import/Configuration/Csv/Map.php b/app/Support/Import/Configuration/Csv/Map.php index bac403e94b..22016210da 100644 --- a/app/Support/Import/Configuration/Csv/Map.php +++ b/app/Support/Import/Configuration/Csv/Map.php @@ -92,7 +92,9 @@ class Map implements ConfigurationInterface } foreach ($this->data as $index => $entry) { $this->data[$index]['values'] = array_unique($this->data[$index]['values']); + asort($this->data[$index]['values']); } + // save number of rows, thus number of steps, in job: $steps = $rowIndex * 5; $extended = $this->job->extended_status; diff --git a/app/Support/Import/Configuration/Csv/Roles.php b/app/Support/Import/Configuration/Csv/Roles.php index 445628aeab..f53a01abf7 100644 --- a/app/Support/Import/Configuration/Csv/Roles.php +++ b/app/Support/Import/Configuration/Csv/Roles.php @@ -135,7 +135,7 @@ class Roles implements ConfigurationInterface $count = $config['column-count']; for ($i = 0; $i < $count; $i++) { $role = $config['column-roles'][$i] ?? '_ignore'; - $mapping = $config['column-do-mapping'][$i] ?? false; + $mapping = $config['column-do-mapping'][$i] ?? false; if ($role === '_ignore' && $mapping === true) { $mapping = false; @@ -160,7 +160,7 @@ class Roles implements ConfigurationInterface $count = $config['column-count']; $toBeMapped = 0; for ($i = 0; $i < $count; $i++) { - $mapping = $config['column-do-mapping'][$i] ?? false; + $mapping = $config['column-do-mapping'][$i] ?? false; if ($mapping === true) { $toBeMapped++; } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index dc28a613db..464fbed037 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -96,7 +96,7 @@ class Navigation // if the range is custom, the end of the period // is another X days (x is the difference between start) // and end added to $theCurrentEnd - if ($repeatFreq == 'custom') { + if ($repeatFreq === 'custom') { /** @var Carbon $tStart */ $tStart = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $tEnd */ @@ -393,7 +393,7 @@ class Navigation return $date; } - if ($repeatFreq == 'half-year' || $repeatFreq == '6M') { + if ($repeatFreq === 'half-year' || $repeatFreq === '6M') { $month = $date->month; $date->startOfYear(); if ($month >= 7) { @@ -496,7 +496,7 @@ class Navigation return $end; } - if ($range == '6M') { + if ($range === '6M') { if ($start->month >= 7) { $end->endOfYear(); @@ -532,7 +532,7 @@ class Navigation return $start; } - if ($range == '6M') { + if ($range === '6M') { if ($start->month >= 7) { $start->startOfYear()->addMonths(6); diff --git a/app/Support/Search/Modifier.php b/app/Support/Search/Modifier.php index 4422cf2e56..acfae12cf6 100644 --- a/app/Support/Search/Modifier.php +++ b/app/Support/Search/Modifier.php @@ -86,17 +86,17 @@ class Modifier case 'date': case 'on': $res = self::sameDate($transaction->date, $modifier['value']); - Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true))); + Log::debug(sprintf('Date same as %s? %s', $modifier['value'], var_export($res, true))); break; case 'date_before': case 'before': $res = self::dateBefore($transaction->date, $modifier['value']); - Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true))); + Log::debug(sprintf('Date before %s? %s', $modifier['value'], var_export($res, true))); break; case 'date_after': case 'after': $res = self::dateAfter($transaction->date, $modifier['value']); - Log::debug(sprintf('Date is %s? %s', $modifier['value'], var_export($res, true))); + Log::debug(sprintf('Date before %s? %s', $modifier['value'], var_export($res, true))); break; } diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index 490e24708a..ce7b271973 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -17,11 +17,6 @@ namespace FireflyIII\Support\Search; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Filter\InternalTransferFilter; -use FireflyIII\Models\Account; -use FireflyIII\Models\AccountType; -use FireflyIII\Models\Budget; -use FireflyIII\Models\Category; -use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\User; use Illuminate\Support\Collection; @@ -98,112 +93,20 @@ class Search implements SearchInterface } } - /** - * @return Collection - */ - public function searchAccounts(): Collection - { - $words = $this->words; - $accounts = $this->user->accounts() - ->accountTypeIn([AccountType::DEFAULT, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE, AccountType::BENEFICIARY]) - ->get(['accounts.*']); - /** @var Collection $result */ - $result = $accounts->filter( - function (Account $account) use ($words) { - if ($this->strpos_arr(strtolower($account->name), $words)) { - return $account; - } - - return false; - } - ); - - $result = $result->slice(0, $this->limit); - - return $result; - } - - /** - * @return Collection - */ - public function searchBudgets(): Collection - { - /** @var Collection $set */ - $set = auth()->user()->budgets()->get(); - $words = $this->words; - /** @var Collection $result */ - $result = $set->filter( - function (Budget $budget) use ($words) { - if ($this->strpos_arr(strtolower($budget->name), $words)) { - return $budget; - } - - return false; - } - ); - - $result = $result->slice(0, $this->limit); - - return $result; - } - - /** - * @return Collection - */ - public function searchCategories(): Collection - { - $words = $this->words; - $categories = $this->user->categories()->get(); - /** @var Collection $result */ - $result = $categories->filter( - function (Category $category) use ($words) { - if ($this->strpos_arr(strtolower($category->name), $words)) { - return $category; - } - - return false; - } - ); - $result = $result->slice(0, $this->limit); - - return $result; - } - - /** - * @return Collection - */ - public function searchTags(): Collection - { - $words = $this->words; - $tags = $this->user->tags()->get(); - /** @var Collection $result */ - $result = $tags->filter( - function (Tag $tag) use ($words) { - if ($this->strpos_arr(strtolower($tag->tag), $words)) { - return $tag; - } - - return false; - } - ); - $result = $result->slice(0, $this->limit); - - return $result; - } - /** * @return Collection */ public function searchTransactions(): Collection { + Log::debug('Start of searchTransactions()'); $pageSize = 100; $processed = 0; $page = 1; $result = new Collection(); + $startTime = microtime(true); do { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser($this->user); $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page); if ($this->hasModifiers()) { $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); @@ -247,7 +150,11 @@ class Search implements SearchInterface Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); - } while (!$reachedEndOfList && !$foundEnough); + // break at some point so the script does not crash: + $currentTime = microtime(true) - $startTime; + Log::debug(sprintf('Have been running for %f seconds.', $currentTime)); + + } while (!$reachedEndOfList && !$foundEnough && $currentTime <= 30); $result = $result->slice(0, $this->limit); diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index a06ea95d27..d709d02b67 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -38,25 +38,6 @@ interface SearchInterface */ public function parseQuery(string $query); - /** - * @return Collection - */ - public function searchAccounts(): Collection; - - /** - * @return Collection - */ - public function searchBudgets(): Collection; - - /** - * @return Collection - */ - public function searchCategories(): Collection; - - /** - * @return Collection - */ - public function searchTags(): Collection; /** * @return Collection diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 8a3ca9c885..c77e3cf3be 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -19,6 +19,7 @@ use Crypt; use DB; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Collection; /** @@ -345,4 +346,20 @@ class Steam return $amount; } + /** + * @param $value + * + * @return mixed + */ + public function tryDecrypt($value) + { + try { + $value = Crypt::decrypt($value); + } catch (DecryptException $e) { + // do not care. + } + + return $value; + } + } diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 9603e54e44..09183c84e5 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -110,7 +110,7 @@ class General extends Twig_Extension $what = $args[2]; // name of the route. $activeWhat = $context['what'] ?? false; - if ($what == $activeWhat && !(strpos(Route::getCurrentRoute()->getName(), $route) === false)) { + if ($what === $activeWhat && !(strpos(Route::getCurrentRoute()->getName(), $route) === false)) { return 'active'; } @@ -132,7 +132,7 @@ class General extends Twig_Extension $args = func_get_args(); $route = $args[0]; // name of the route. - if (Route::getCurrentRoute()->getName() == $route) { + if (Route::getCurrentRoute()->getName() === $route) { return 'active'; } diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index d3c0f314e5..82a5553c4d 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -51,7 +51,7 @@ class Journal extends Twig_Extension $array = []; /** @var Account $entry */ foreach ($list as $entry) { - if ($entry->accountType->type == AccountType::CASH) { + if ($entry->accountType->type === AccountType::CASH) { $array[] = '(cash)'; continue; } @@ -123,7 +123,7 @@ class Journal extends Twig_Extension $array = []; /** @var Account $entry */ foreach ($list as $entry) { - if ($entry->accountType->type == 'Cash account') { + if ($entry->accountType->type === AccountType::CASH) { $array[] = '(cash)'; continue; } diff --git a/app/Support/Twig/Rule.php b/app/Support/Twig/Rule.php index 2503cec452..9924e6f7a0 100644 --- a/app/Support/Twig/Rule.php +++ b/app/Support/Twig/Rule.php @@ -70,7 +70,7 @@ class Rule extends Twig_Extension $ruleTriggers = array_keys(Config::get('firefly.rule-triggers')); $possibleTriggers = []; foreach ($ruleTriggers as $key) { - if ($key != 'user_action') { + if ($key !== 'user_action') { $possibleTriggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice'); } } diff --git a/app/User.php b/app/User.php index 3c045f2412..da5e565d38 100644 --- a/app/User.php +++ b/app/User.php @@ -149,7 +149,7 @@ class User extends Authenticatable { foreach ($this->roles as $role) { - if ($role->name == $name) { + if ($role->name === $name) { return true; } } diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index cf4b60c3f9..e08c95a22a 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -211,7 +211,7 @@ class FireflyValidator extends Validator // count budgets, should have at least one $count = $budgets->filter( function (Budget $budget) use ($value) { - return $budget->name == $value; + return $budget->name === $value; } )->count(); @@ -329,7 +329,7 @@ class FireflyValidator extends Validator /** @var AccountMeta $entry */ foreach ($set as $entry) { - if ($entry->data == $value) { + if ($entry->data === $value) { return false; } @@ -398,7 +398,7 @@ class FireflyValidator extends Validator /** @var PiggyBank $entry */ foreach ($set as $entry) { $fieldValue = $this->tryDecrypt($entry->name); - if ($fieldValue == $value) { + if ($fieldValue === $value) { return false; } } @@ -460,7 +460,7 @@ class FireflyValidator extends Validator $set = $user->accounts()->where('account_type_id', $type->id)->get(); /** @var Account $entry */ foreach ($set as $entry) { - if ($entry->name == $value) { + if ($entry->name === $value) { return false; } } @@ -486,7 +486,7 @@ class FireflyValidator extends Validator $set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); /** @var Account $entry */ foreach ($set as $entry) { - if ($entry->name == $value) { + if ($entry->name === $value) { return false; } } @@ -510,7 +510,7 @@ class FireflyValidator extends Validator $set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); /** @var Account $entry */ foreach ($set as $entry) { - if ($entry->name == $value) { + if ($entry->name === $value) { return false; } } @@ -534,7 +534,7 @@ class FireflyValidator extends Validator $set = auth()->user()->accounts()->where('account_type_id', $type->id)->where('id', '!=', $ignore)->get(); /** @var Account $entry */ foreach ($set as $entry) { - if ($entry->name == $value) { + if ($entry->name === $value) { return false; } } diff --git a/composer.lock b/composer.lock index 1118947c51..2ae1cf8cba 100644 --- a/composer.lock +++ b/composer.lock @@ -220,16 +220,16 @@ }, { "name": "doctrine/cache", - "version": "v1.6.1", + "version": "v1.6.2", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b", + "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b", "shasum": "" }, "require": { @@ -286,7 +286,7 @@ "cache", "caching" ], - "time": "2016-10-29T11:16:17+00:00" + "time": "2017-07-22T12:49:21+00:00" }, { "name": "doctrine/collections", @@ -357,16 +357,16 @@ }, { "name": "doctrine/common", - "version": "v2.7.2", + "version": "v2.7.3", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "930297026c8009a567ac051fd545bf6124150347" + "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", - "reference": "930297026c8009a567ac051fd545bf6124150347", + "url": "https://api.github.com/repos/doctrine/common/zipball/4acb8f89626baafede6ee5475bc5844096eba8a9", + "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9", "shasum": "" }, "require": { @@ -426,20 +426,20 @@ "persistence", "spl" ], - "time": "2017-01-13T14:02:13+00:00" + "time": "2017-07-22T08:35:12+00:00" }, { "name": "doctrine/dbal", - "version": "v2.5.12", + "version": "v2.5.13", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44" + "reference": "729340d8d1eec8f01bff708e12e449a3415af873" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44", - "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/729340d8d1eec8f01bff708e12e449a3415af873", + "reference": "729340d8d1eec8f01bff708e12e449a3415af873", "shasum": "" }, "require": { @@ -497,37 +497,37 @@ "persistence", "queryobject" ], - "time": "2017-02-08T12:53:47+00:00" + "time": "2017-07-22T20:44:48+00:00" }, { "name": "doctrine/inflector", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/e11d84c6e018beedd929cff5220969a3c6d1d462", + "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Inflector\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -564,7 +564,7 @@ "singularize", "string" ], - "time": "2015-11-06T14:35:42+00:00" + "time": "2017-07-22T12:18:28+00:00" }, { "name": "doctrine/lexer", @@ -664,16 +664,16 @@ }, { "name": "laravel/framework", - "version": "v5.4.28", + "version": "v5.4.30", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "442511fc62121085d184355e4f964c88942bbecb" + "reference": "b9a64955f4278f45ac348a6e000b5ecc85da167a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/442511fc62121085d184355e4f964c88942bbecb", - "reference": "442511fc62121085d184355e4f964c88942bbecb", + "url": "https://api.github.com/repos/laravel/framework/zipball/b9a64955f4278f45ac348a6e000b5ecc85da167a", + "reference": "b9a64955f4278f45ac348a6e000b5ecc85da167a", "shasum": "" }, "require": { @@ -789,7 +789,7 @@ "framework", "laravel" ], - "time": "2017-06-30T13:43:07+00:00" + "time": "2017-07-19T19:26:19+00:00" }, { "name": "laravelcollective/html", @@ -916,16 +916,16 @@ }, { "name": "league/csv", - "version": "8.2.1", + "version": "8.2.2", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "43fd8b022815a0758d85e925dd92a43fe0d41bb4" + "reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/43fd8b022815a0758d85e925dd92a43fe0d41bb4", - "reference": "43fd8b022815a0758d85e925dd92a43fe0d41bb4", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/fa8bc05f64eb6c66b96edfaf60648f022ecb5f55", + "reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55", "shasum": "" }, "require": { @@ -969,7 +969,7 @@ "read", "write" ], - "time": "2017-02-23T08:25:03+00:00" + "time": "2017-07-12T07:18:20+00:00" }, { "name": "league/flysystem", @@ -1636,7 +1636,7 @@ }, { "name": "symfony/console", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", @@ -1758,7 +1758,7 @@ }, { "name": "symfony/debug", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", @@ -1814,7 +1814,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.24", + "version": "v2.8.25", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1874,7 +1874,7 @@ }, { "name": "symfony/finder", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -1923,16 +1923,16 @@ }, { "name": "symfony/http-foundation", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "f347a5f561b03db95ed666959db42bbbf429b7e5" + "reference": "e307abe4b79ccbbfdced9b91c132fd128f456bc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f347a5f561b03db95ed666959db42bbbf429b7e5", - "reference": "f347a5f561b03db95ed666959db42bbbf429b7e5", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e307abe4b79ccbbfdced9b91c132fd128f456bc5", + "reference": "e307abe4b79ccbbfdced9b91c132fd128f456bc5", "shasum": "" }, "require": { @@ -1972,7 +1972,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2017-06-24T09:29:48+00:00" + "time": "2017-07-17T14:07:10+00:00" }, { "name": "symfony/http-kernel", @@ -2228,16 +2228,16 @@ }, { "name": "symfony/process", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5ab8949b682b1bf9d4511a228b5e045c96758c30" + "reference": "07432804942b9f6dd7b7377faf9920af5f95d70a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5ab8949b682b1bf9d4511a228b5e045c96758c30", - "reference": "5ab8949b682b1bf9d4511a228b5e045c96758c30", + "url": "https://api.github.com/repos/symfony/process/zipball/07432804942b9f6dd7b7377faf9920af5f95d70a", + "reference": "07432804942b9f6dd7b7377faf9920af5f95d70a", "shasum": "" }, "require": { @@ -2273,11 +2273,11 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-07-03T08:12:02+00:00" + "time": "2017-07-13T13:05:09+00:00" }, { "name": "symfony/routing", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", @@ -2355,7 +2355,7 @@ }, { "name": "symfony/translation", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", @@ -2420,16 +2420,16 @@ }, { "name": "symfony/var-dumper", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9ee920bba1d2ce877496dcafca7cbffff4dbe08a" + "reference": "0f32b62d21991700250fed5109b092949007c5b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9ee920bba1d2ce877496dcafca7cbffff4dbe08a", - "reference": "9ee920bba1d2ce877496dcafca7cbffff4dbe08a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0f32b62d21991700250fed5109b092949007c5b3", + "reference": "0f32b62d21991700250fed5109b092949007c5b3", "shasum": "" }, "require": { @@ -2484,7 +2484,7 @@ "debug", "dump" ], - "time": "2017-07-05T13:02:37+00:00" + "time": "2017-07-10T14:18:27+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -2698,16 +2698,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v2.4.1", + "version": "v2.4.3", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "af98b3a4ccac9364f2145fae974ff3392ec402b1" + "reference": "d7c88f08131f6404cb714f3f6cf0642f6afa3903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/af98b3a4ccac9364f2145fae974ff3392ec402b1", - "reference": "af98b3a4ccac9364f2145fae974ff3392ec402b1", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/d7c88f08131f6404cb714f3f6cf0642f6afa3903", + "reference": "d7c88f08131f6404cb714f3f6cf0642f6afa3903", "shasum": "" }, "require": { @@ -2717,19 +2717,6 @@ "symfony/finder": "~2.7|~3.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, - "laravel": { - "providers": [ - "Barryvdh\\Debugbar\\ServiceProvider" - ], - "aliases": { - "Debugbar": "Barryvdh\\Debugbar\\Facade" - } - } - }, "autoload": { "psr-4": { "Barryvdh\\Debugbar\\": "src/" @@ -2756,20 +2743,20 @@ "profiler", "webprofiler" ], - "time": "2017-06-14T07:44:44+00:00" + "time": "2017-07-21T11:56:48+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.4.0", + "version": "v2.4.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "87a02ff574f722c685e011f76692ab869ab64f6c" + "reference": "2b1273c45e2f8df7a625563e2283a17c14f02ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/87a02ff574f722c685e011f76692ab869ab64f6c", - "reference": "87a02ff574f722c685e011f76692ab869ab64f6c", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/2b1273c45e2f8df7a625563e2283a17c14f02ae8", + "reference": "2b1273c45e2f8df7a625563e2283a17c14f02ae8", "shasum": "" }, "require": { @@ -2829,7 +2816,7 @@ "phpstorm", "sublime" ], - "time": "2017-06-16T14:08:59+00:00" + "time": "2017-07-16T00:24:12+00:00" }, { "name": "barryvdh/reflection-docblock", @@ -3223,7 +3210,7 @@ ], "authors": [ { - "name": "Pádraic Brady", + "name": "Padraic Brady", "email": "padraic.brady@gmail.com", "homepage": "http://blog.astrumfutura.com" }, @@ -3347,22 +3334,22 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/46f7e8bb075036c92695b15a1ddb6971c751e585", + "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585", "shasum": "" }, "require": { "php": ">=5.5", "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { @@ -3388,24 +3375,24 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30T07:12:33+00:00" + "time": "2017-07-15T11:38:20+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.2.1", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": ">=5.5", + "php": "^5.5 || ^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { @@ -3435,7 +3422,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2016-11-25T06:54:22+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -4463,7 +4450,7 @@ }, { "name": "symfony/class-loader", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", @@ -4519,7 +4506,7 @@ }, { "name": "symfony/config", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -4637,16 +4624,16 @@ }, { "name": "symfony/filesystem", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "311fa718389efbd8b627c272b9324a62437018cc" + "reference": "427987eb4eed764c3b6e38d52a0f87989e010676" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/311fa718389efbd8b627c272b9324a62437018cc", - "reference": "311fa718389efbd8b627c272b9324a62437018cc", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/427987eb4eed764c3b6e38d52a0f87989e010676", + "reference": "427987eb4eed764c3b6e38d52a0f87989e010676", "shasum": "" }, "require": { @@ -4682,11 +4669,11 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-06-24T09:29:48+00:00" + "time": "2017-07-11T07:17:58+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -4735,7 +4722,7 @@ }, { "name": "symfony/yaml", - "version": "v3.3.4", + "version": "v3.3.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", diff --git a/config/firefly.php b/config/firefly.php index 5fffbbf7a8..7176320a68 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -23,7 +23,7 @@ return [ 'is_demo_site' => false, ], 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), - 'version' => '4.6.2', + 'version' => '4.6.3', 'maxUploadSize' => 15242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], 'list_length' => 10, @@ -109,7 +109,8 @@ return [ 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true], 'pl_PL' => ['name_locale' => 'Polski', 'name_english' => 'Polish ', 'complete' => false], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portuguese (Brazil)', 'complete' => true], - 'sl-SI' => ['name_locale' => 'Slovenščina', 'name_english' => 'Slovenian', 'complete' => false], + 'ru_RU' => ['name_locale' => 'Русский', 'name_english' => 'Russian', 'complete' => false], + 'sl_SI' => ['name_locale' => 'Slovenščina', 'name_english' => 'Slovenian', 'complete' => false], 'zh-TW' => ['name_locale' => '正體中文', 'name_english' => 'Chinese Traditional', 'complete' => false], ], 'transactionTypesByWhat' => [ diff --git a/config/intro.php b/config/intro.php new file mode 100644 index 0000000000..6ac626f98e --- /dev/null +++ b/config/intro.php @@ -0,0 +1,159 @@ + [ + 'intro' => [], + 'accounts-chart' => ['element' => '#accounts-chart'], + 'box_out_holder' => ['element' => '#box_out_holder'], + 'help' => ['element' => '#help', 'position' => 'bottom'], + 'sidebar-toggle' => ['element' => '#sidebar-toggle', 'position' => 'bottom'], + 'outro' => [], + ], + // accounts: create + 'accounts_create' => [ + 'iban' => ['element' => '#ffInput_iban'], + ], + // extra text for asset account creation. + 'accounts_create_asset' => [ + 'opening_balance' => ['element' => '#ffInput_openingBalance'], + 'currency' => ['element' => '#ffInput_currency_id'], + 'virtual' => ['element' => '#ffInput_virtualBalance'], + ], + + // budgets: index + 'budgets_index' => [ + 'intro' => [], + 'set_budget' => ['element' => '#availableBar',], + 'see_expenses_bar' => ['element' => '#spentBar'], + 'navigate_periods' => ['element' => '#periodNavigator'], + 'new_budget' => ['element' => '#createBudgetBox'], + 'list_of_budgets' => ['element' => '#budgetList'], + + ], + // reports: index, default report, audit, budget, cat, tag + 'reports_index' => [ + 'intro' => [], + 'inputReportType' => ['element' => '#inputReportType'], + 'inputAccountsSelect' => ['element' => '#inputAccountsSelect'], + 'inputDateRange' => ['element' => '#inputDateRange'], + 'extra-options-box' => ['element' => '#extra-options-box', 'position' => 'top'], + ], + 'reports_report_default' => [ + 'intro' => [], + ], + 'reports_report_audit' => [ + 'intro' => [], + 'optionsBox' => ['element' => '#optionsBox'], + ], + 'reports_report_category' => [ + 'intro' => [], + 'pieCharts' => ['element' => '#pieCharts'], + 'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'], + + ], + 'reports_report_tag' => [ + 'intro' => [], + 'pieCharts' => ['element' => '#pieCharts'], + 'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'], + ], + 'reports_report_budget' => [ + 'intro' => [], + 'pieCharts' => ['element' => '#pieCharts'], + 'incomeAndExpensesChart' => ['element' => '#incomeAndExpensesChart', 'position' => 'top'], + ], + + // transactions: create (also per type!) + 'transactions_create' => [ + 'switch_box' => ['element' => '#switch-box'], + 'ffInput_category' => ['element' => '#ffInput_category'], + ], + 'transactions_create_withdrawal' => [ + 'ffInput_budget' => ['element' => '#ffInput_budget_id'], + 'currency_dropdown_amount' => ['element' => '#currency_dropdown_amount'], + ], + 'transactions_create_deposit' => [ + 'currency_dropdown_amount' => ['element' => '#currency_dropdown_amount'], + ], + 'transactions_create_transfer' => [ + 'ffInput_piggy_bank_id' => ['element' => '#ffInput_piggy_bank_id'], + ], + + // piggies: index, create, show + 'piggy-banks_index' => [ + 'saved' => ['element' => '.piggySaved'], + 'button' => ['element' => '.piggyBar',], + 'accountStatus' => ['element' => '#accountStatus', 'position' => 'top'], + ], + 'piggy-banks_create' => [ + 'name' => ['element' => '#ffInput_name'], + 'date' => ['element' => '#ffInput_targetdate'], + + ], + 'piggy-banks_show' => [ + 'intro' => [], + 'piggyChart' => ['element' => '#piggyChart'], + 'piggyDetails' => ['element' => '#piggyDetails'], + 'piggyEvents' => ['element' => '#piggyEvents'], + ], + + // bills: index, create, show + 'bills_index' => [ + 'paid_in_period' => ['element' => '.paid_in_period'], + 'expected_in_period' => ['element' => '.expected_in_period'], + ], + 'bills_create' => [ + 'name' => ['element' => '#name_holder'], + 'match' => ['element' => '#match_holder'], + 'amount_min_holder' => ['element' => '#amount_min_holder'], + 'repeat_freq_holder' => ['element' => '#repeat_freq_holder'], + 'skip_holder' => ['element' => '#skip_holder'], + ], + 'bills_show' => [ + 'billInfo' => ['element' => '#billInfo'], + 'billButtons' => ['element' => '#billButtons'], + 'billChart' => ['element' => '#billChart', 'position' => 'top'], + + ], + // rules: index, create-rule, edit-rule + 'rules_index' => [ + 'intro' => [], + 'new_rule_group' => ['element' => '#new_rule_group'], + 'new_rule' => ['element' => '.new_rule'], + 'prio_buttons' => ['element' => '.prio_buttons'], + 'test_buttons' => ['element' => '.test_buttons'], + 'rule-triggers' => ['element' => '.rule-triggers'], + 'outro' => [], + ], + 'rules_create' => [ + 'mandatory' => ['element' => '#mandatory'], + 'ruletriggerholder' => ['element' => '.rule-trigger-box'], + 'test_rule_triggers' => ['element' => '.test_rule_triggers'], + 'actions' => ['element' => '.rule-action-box', 'position' => 'top'], + ], + // preferences: index + 'preferences_index' => [ + 'tabs' => ['element' => '.nav-tabs'], + ], + // currencies: index, create + 'currencies_index' => [ + 'intro' => [], + 'default' => ['element' => '.defaultCurrency'], + ], + 'currencies_create' => [ + 'code' => ['element' => '#ffInput_code',], + ], +]; \ No newline at end of file diff --git a/config/twigbridge.php b/config/twigbridge.php index 095c342c9c..b6470423d3 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -156,6 +156,7 @@ return [ 'Preferences', 'URL', 'Config', + 'Request', 'ExpandedForm' => [ 'is_safe' => [ 'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location', diff --git a/config/upgrade.php b/config/upgrade.php index 58c0ad66bd..aed71295bf 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -15,11 +15,13 @@ return [ 'text' => [ 'upgrade' => [ - '4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.', + '4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.', + '4.6.3' => 'This will be the last version to require PHP7.0. Future versions will require PHP7.1 minimum.', ], 'install' => [ - '4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.', + '4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.', + '4.6.3' => 'This will be the last version to require PHP7.0. Future versions will require PHP7.1 minimum.', ], ], ]; diff --git a/public/css/bootstrap-tour.min.css b/public/css/bootstrap-tour.min.css deleted file mode 100755 index 8ebd3d3750..0000000000 --- a/public/css/bootstrap-tour.min.css +++ /dev/null @@ -1,22 +0,0 @@ -/* ======================================================================== - * bootstrap-tour - v0.10.3 - * http://bootstraptour.com - * ======================================================================== - * Copyright 2012-2015 Ulrich Sossou - * - * ======================================================================== - * Licensed under the MIT License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/MIT - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ======================================================================== - */ - -.tour-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1100;background-color:#000;opacity:.8;filter:alpha(opacity=80)}.tour-step-backdrop{position:relative;z-index:1101}.tour-step-backdrop>td{position:relative;z-index:1101}.tour-step-background{position:absolute!important;z-index:1100;background:inherit;border-radius:6px}.popover[class*=tour-]{z-index:1102}.popover[class*=tour-] .popover-navigation{padding:9px 14px;overflow:hidden}.popover[class*=tour-] .popover-navigation [data-role=end]{float:right}.popover[class*=tour-] .popover-navigation [data-role=prev],.popover[class*=tour-] .popover-navigation [data-role=next],.popover[class*=tour-] .popover-navigation [data-role=end]{cursor:pointer}.popover[class*=tour-] .popover-navigation [data-role=prev].disabled,.popover[class*=tour-] .popover-navigation [data-role=next].disabled,.popover[class*=tour-] .popover-navigation [data-role=end].disabled{cursor:default}.popover[class*=tour-].orphan{position:fixed;margin-top:0}.popover[class*=tour-].orphan .arrow{display:none} \ No newline at end of file diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 5e5bf17ce0..9e545d8f57 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -7,7 +7,7 @@ * * See the LICENSE file for details. */ -/** global: Chart, defaultChartOptions, accounting, defaultPieOptions, noDataForChart */ +/** global: Chart, defaultChartOptions, accounting, defaultPieOptions, noDataForChart, noDataForChart */ var allCharts = {}; /* @@ -38,7 +38,7 @@ var strokePointHighColors = []; for (var i = 0; i < colourSet.length; i++) { - fillColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.2)"); + fillColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.5)"); strokePointHighColors.push("rgba(" + colourSet[i][0] + ", " + colourSet[i][1] + ", " + colourSet[i][2] + ", 0.9)"); } @@ -60,7 +60,8 @@ function colorizeData(data) { for (var i = 0; i < data.count; i++) { newData.labels = data.labels; var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; + dataset.fill = false; + dataset.backgroundColor = dataset.borderColor = fillColors[i]; newData.datasets.push(dataset); } return newData; @@ -239,13 +240,14 @@ function pieChart(URI, container) { * @param colorData */ function drawAChart(URI, container, chartType, options, colorData) { - if ($('#' + container).length === 0) { + var containerObj = $('#' + container); + if (containerObj.length === 0) { return; } $.getJSON(URI).done(function (data) { - $('#' + container).removeClass('general-chart-error'); + containerObj.removeClass('general-chart-error'); if (data.labels.length === 0) { // remove the chart container + parent var holder = $('#' + container).parent().parent(); diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index c0a223edf0..ea3491dbfd 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -15,6 +15,11 @@ $(function () { configAccounting(currencySymbol); + // on submit of form, disable any button in form: + $('form.form-horizontal').on('submit',function() { + $('button[type="submit"]').prop('disabled',true); + }); + $.ajaxSetup({ headers: { 'X-CSRF-Token': $('meta[name="_token"]').attr('content') diff --git a/public/js/ff/help.js b/public/js/ff/help.js index 536a4c5c2e..a8393904dd 100644 --- a/public/js/ff/help.js +++ b/public/js/ff/help.js @@ -11,27 +11,39 @@ $(function () { "use strict"; $('#help').click(showHelp); - $(function () { - //$('[data-toggle="tooltip"]').tooltip(); - }); }); function showHelp(e) { "use strict"; var target = $(e.target); var route = target.data('route'); - // - $('#helpBody').html(''); - $('#helpTitle').html('Please hold...'); + var specialPage = target.data('extra'); + if (typeof specialPage === 'undefined') { + specialPage = ''; + } + $('#helpBody').html(''); $('#helpModal').modal('show'); $('#helpTitle').html('Help for this page'); $.getJSON('help/' + encodeURI(route)).done(function (data) { - $('#helpBody').html(data); + $('#helpBody').html(data.html); }).fail(function () { $('#helpBody').html('No help text could be found.
'); $('#helpTitle').html('Apologies'); }); + $('#reenableGuidance').unbind('click').click(function () { + enableGuidance(route, specialPage); + return false; + }); return false; } + +function enableGuidance(route, specialPage) { + $.post('json/intro/enable/' + route + '/' + specialPage).done(function (data) { + alert(data.message); + }).fail(function () { + alert('Could not re-enable introduction.'); + }); +} + diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index f8a991074d..665fcbca69 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -60,6 +60,8 @@ function failedJobImport(jqxhr, textStatus, error) { function reportOnJobImport(data) { switch (data.status) { + default: + break; case "configured": // job is ready. Do not check again, just show the start-box. Hide the rest. $('.statusbox').hide(); @@ -173,6 +175,7 @@ function updateBar(data) { bar.removeClass('progress-bar-success').addClass('progress-bar-info'); bar.attr('aria-valuenow', 100); bar.css('width', '100%'); + return true; } /** @@ -214,6 +217,4 @@ function reportOnErrors(data) { $('#import-status-error-list').append(item); } } - return; - } \ No newline at end of file diff --git a/public/js/ff/index.js b/public/js/ff/index.js index 242768712d..58005bac67 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -8,36 +8,15 @@ * See the LICENSE file for details. */ -/** global: Tour, showTour, accountFrontpageUri, token, billCount, accountExpenseUri, accountRevenueUri */ +/** global: accountFrontpageUri, token, billCount, accountExpenseUri, accountRevenueUri */ $(function () { "use strict"; // do chart JS stuff. drawChart(); - if (showTour === true) { - $.getJSON('json/tour').done(function (data) { - var tour = new Tour( - { - steps: data.steps, - template: data.template, - onEnd: endTheTour - }); - // Initialize the tour - tour.init(); - // Start the tour - tour.start(); - }); - } - }); -function endTheTour() { - "use strict"; - $.post('json/end-tour', {_token: token}); - -} - function drawChart() { "use strict"; lineChart(accountFrontpageUri, 'accounts-chart'); diff --git a/public/js/ff/intro/intro.js b/public/js/ff/intro/intro.js new file mode 100644 index 0000000000..70a5354c28 --- /dev/null +++ b/public/js/ff/intro/intro.js @@ -0,0 +1,34 @@ +/* + * intro.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: routeForTour, routeStepsUri, routeForFinishedTour, forceDemoOff */ + +$(function () { + "use strict"; + if(!forceDemoOff) { + $.getJSON(routeStepsUri).done(setupIntro) + } +}); + +function setupIntro(steps) { + + var intro = introJs(); + intro.setOptions({ + steps: steps, + exitOnEsc: true, + exitOnOverlayClick: true, + keyboardNavigation: true + }); + intro.oncomplete(reportIntroFinished); + intro.onexit(reportIntroFinished); + intro.start(); +} + +function reportIntroFinished() { + $.post(routeForFinishedTour); +} \ No newline at end of file diff --git a/public/js/ff/rules/index.js b/public/js/ff/rules/index.js index a64d6a6cc6..52dd0ef27e 100644 --- a/public/js/ff/rules/index.js +++ b/public/js/ff/rules/index.js @@ -37,9 +37,40 @@ $(function () { } ); + + // test rule triggers button: + $('.test_rule_triggers').click(testRuleTriggers); } ); +function testRuleTriggers(e) { + var obj = $(e.target); + var ruleId = parseInt(obj.data('id')); + + // Find a list of existing transactions that match these triggers + $.get('rules/test-rule/' + ruleId).done(function (data) { + var modal = $("#testTriggerModal"); + + // Set title and body + modal.find(".transactions-list").html(data.html); + + // Show warning if appropriate + if (data.warning) { + modal.find(".transaction-warning .warning-contents").text(data.warning); + modal.find(".transaction-warning").show(); + } else { + modal.find(".transaction-warning").hide(); + } + + // Show the modal dialog + modal.modal(); + }).fail(function () { + alert('Cannot get transactions for given triggers.'); + }); + + return false; +} + function sortStop(event, ui) { "use strict"; diff --git a/public/js/ff/search/index.js b/public/js/ff/search/index.js new file mode 100644 index 0000000000..06a8f26697 --- /dev/null +++ b/public/js/ff/search/index.js @@ -0,0 +1,35 @@ +/* + * index.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: searchQuery,searchUri */ + + + +$(function () { + "use strict"; + startSearch(searchQuery); + +}); + +function startSearch(query) { + + $.post(searchUri, {query: query}).done(presentSearchResults).fail(searchFailure); +} + +function searchFailure() { + $('.result_row').hide(); + $('.error_row').show(); +} + +function presentSearchResults(data) { + $('.search_ongoing').hide(); + $('p.search_count').show(); + $('span.search_count').text(data.count); + $('.search_box').find('.overlay').remove(); + $('.search_results').html(data.html).show(); +} \ No newline at end of file diff --git a/public/js/ff/tags/show.js b/public/js/ff/tags/show.js index 0cf25a8a26..bcf22dee9c 100644 --- a/public/js/ff/tags/show.js +++ b/public/js/ff/tags/show.js @@ -12,7 +12,6 @@ Some vars as prep for the map: */ var map; -var markers = []; var setTag = false; var mapOptions = { diff --git a/public/js/ff/transactions/single/create.js b/public/js/ff/transactions/single/create.js index 08d2f9988d..f1b375bb92 100644 --- a/public/js/ff/transactions/single/create.js +++ b/public/js/ff/transactions/single/create.js @@ -6,7 +6,7 @@ * See the LICENSE file for details. */ -/** global: currencyInfo, accountInfo, what,Modernizr, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, middleCrumbUrl,exchangeRateInstructions, convertForeignToNative, convertSourceToDestination, selectsForeignCurrency, accountInfo */ +/** global: currencyInfo, overruleCurrency,useAccountCurrency, accountInfo, what,Modernizr, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, middleCrumbUrl,exchangeRateInstructions, convertForeignToNative, convertSourceToDestination, selectsForeignCurrency, accountInfo */ $(document).ready(function () { "use strict"; diff --git a/public/js/lib/bootstrap-tour.min.js b/public/js/lib/bootstrap-tour.min.js deleted file mode 100755 index a481cecfb4..0000000000 --- a/public/js/lib/bootstrap-tour.min.js +++ /dev/null @@ -1,22 +0,0 @@ -/* ======================================================================== - * bootstrap-tour - v0.10.3 - * http://bootstraptour.com - * ======================================================================== - * Copyright 2012-2015 Ulrich Sossou - * - * ======================================================================== - * Licensed under the MIT License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/MIT - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ======================================================================== - */ - -!function(t,e){return"function"==typeof define&&define.amd?define(["jquery"],function(o){return t.Tour=e(o)}):"object"==typeof exports?module.exports=e(require("jQuery")):t.Tour=e(t.jQuery)}(window,function(t){var e,o;return o=window.document,e=function(){function e(e){var o;try{o=window.localStorage}catch(n){o=!1}this._options=t.extend({name:"tour",steps:[],container:"body",autoscroll:!0,keyboard:!0,storage:o,debug:!1,backdrop:!1,backdropContainer:"body",backdropPadding:0,redirect:!0,orphan:!1,duration:!1,delay:!1,basePath:"",template:'storage/logs
for instructions.',
'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.',
- 'warning_much_data' => ':days days of data may take a while to load.',
+ 'warning_much_data' => 'Załadowanie :days dni danych może trochę potrwać.',
'registered' => 'Zarejestrowałeś się pomyślnie!',
- 'search' => 'Szukaj',
- 'search_found_accounts' => 'Znaleziono :count kont(o/a) dla twojego wyszukiwania.',
- 'search_found_categories' => 'Znaleziono :count kategorii dla twojego wyszukiwania.',
- 'search_found_budgets' => 'Znaleziono :count budżet(y/ów) dla twojego wyszukiwania.',
- 'search_found_tags' => 'Znaleziono :count tag(i/ów) dla twojego wyszukiwania.',
- 'search_found_transactions' => 'Znaleziono :count transakcji dla twojego wyszukiwania.',
- 'results_limited' => 'Wyniki są ograniczone do :count wpisów.',
'tagbalancingAct' => 'Balancing act',
'tagadvancePayment' => 'Advance payment',
'tagnothing' => '',
@@ -88,6 +80,8 @@ return [
'user_id_is' => 'Twój id użytkownika to :user',
'field_supports_markdown' => 'To pole obsługuje Markdown.',
'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.',
+ 'reenable_intro_text' => 'You can also reenable the introduction guidance.',
+ 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.',
'nothing_to_display' => 'There are no transactions to show you',
'show_all_no_filter' => 'Pokaż wszystkie transakcje bez grupowania ich według daty.',
'expenses_by_category' => 'Wydatki wg kategorii',
@@ -127,30 +121,39 @@ return [
'all_journals_without_budget' => 'All transactions without a budget',
'journals_without_budget' => 'Transactions without a budget',
'all_journals_without_category' => 'All transactions without a category',
- 'journals_without_category' => 'Transactions without a category',
- 'all_journals_for_account' => 'All transactions for account :name',
- 'chart_all_journals_for_account' => 'Chart of all transactions for account :name',
- 'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
+ 'journals_without_category' => 'Transakcje bez kategorii',
+ 'all_journals_for_account' => 'Wszystkie transakcje dla konta :name',
+ 'chart_all_journals_for_account' => 'Wykres wszystkich transakcji dla konta :name',
+ 'journals_in_period_for_account' => 'Wszystkie transakcje dla konta :name od :start do :end',
'transferred' => 'Transferred',
- 'all_withdrawal' => 'All expenses',
- 'all_transactions' => 'All transactions',
+ 'all_withdrawal' => 'Wszystkie wydatki',
+ 'all_transactions' => 'Wszystkie transakcje',
'title_withdrawal_between' => 'All expenses between :start and :end',
- 'all_deposit' => 'All revenue',
+ 'all_deposit' => 'Wszystkie przychody',
'title_deposit_between' => 'All revenue between :start and :end',
- 'all_transfers' => 'All transfers',
+ 'all_transfers' => 'Wszystkie transfery',
'title_transfers_between' => 'All transfers between :start and :end',
- 'all_transfer' => 'All transfers',
- 'all_journals_for_tag' => 'All transactions for tag ":tag"',
- 'title_transfer_between' => 'All transfers between :start and :end',
- 'all_journals_for_category' => 'All transactions for category :name',
- 'all_journals_for_budget' => 'All transactions for budget :name',
- 'chart_all_journals_for_budget' => 'Chart of all transactions for budget :name',
- 'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
- 'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end',
- 'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
+ 'all_transfer' => 'Wszystkie transfery',
+ 'all_journals_for_tag' => 'Wszystkie transakcje dla tagu ":tag"',
+ 'title_transfer_between' => 'Wszystkie transfery od :start do :end',
+ 'all_journals_for_category' => 'Wszystkie transakcje dla kategorii :name',
+ 'all_journals_for_budget' => 'Wszystkie transakcje dla budżetu :name',
+ 'chart_all_journals_for_budget' => 'Wykres wszystkich transakcji dla budżetu :name',
+ 'journals_in_period_for_category' => 'Wszystkie transakcje dla kategorii :name od :start do :end',
+ 'journals_in_period_for_tag' => 'Wszystkie transakcje dla tagu :name od :start do :end',
+ 'not_available_demo_user' => 'Funkcja, do której próbujesz uzyskać dostęp nie jest dostępna dla użytkowników demo.',
'exchange_rate_instructions' => 'Asset account "@name" only accepts transactions in @native_currency. If you wish to use @foreign_currency instead, make sure that the amount in @native_currency is known as well:',
'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.',
- 'transaction_data' => 'Transaction data',
+ 'transaction_data' => 'Dane transakcji',
+
+ // search
+ 'search' => 'Szukaj',
+ 'search_found_transactions' => 'Number of transactions found:',
+ 'general_search_error' => 'An error occured while searching. Please check the log files for more information.',
+ 'search_box' => 'Search',
+ 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.',
+ 'search_error' => 'Error while searching',
+ 'search_searching' => 'Searching ...',
// repeat frequencies:
'repeat_freq_yearly' => 'rocznie',
@@ -187,11 +190,11 @@ return [
'export_status_collecting_journals' => 'Collecting your transactions...',
'export_status_collected_journals' => 'Collected your transactions!',
'export_status_converting_to_export_format' => 'Converting your transactions...',
- 'export_status_converted_to_export_format' => 'Converted your transactions!',
+ 'export_status_converted_to_export_format' => 'Przekonwertowano twoje transakcje!',
'export_status_creating_journal_file' => 'Tworzenie pliku eksportu...',
'export_status_created_journal_file' => 'Utworzono plik eksportu!',
'export_status_collecting_attachments' => 'Collecting all your attachments...',
- 'export_status_collected_attachments' => 'Collected all your attachments!',
+ 'export_status_collected_attachments' => 'Zebrano wszystkie twoje załączniki!',
'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...',
'export_status_collected_old_uploads' => 'Collected all your previous uploads!',
'export_status_creating_config_file' => 'Tworzenie pliku konfiguracji...',
@@ -204,7 +207,6 @@ return [
// rules
'rules' => 'Reguły',
- 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.',
'rule_name' => 'Nazwa reguły',
'rule_triggers' => 'Reguły są wyzwalane gdy',
'rule_actions' => 'Reguła będzie',
@@ -254,14 +256,14 @@ return [
'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions',
'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.',
'warning_no_valid_triggers' => 'Nie dostarczono prawidłowych wyzwalaczy.',
- 'execute_on_existing_transactions' => 'Wykonaj dla istniejących transakcji',
- 'rule_group_select_transactions' => 'Wykonaj grupę reguł ":title" na istniejących transakcjach',
- 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions',
- 'execute_on_existing_transactions_short' => 'Istniejące transakcje',
- 'executed_group_on_existing_transactions' => 'Wykonano grupę ":title" dla istniejących transakcji',
- 'execute_group_on_existing_transactions' => 'Wykonaj grupę ":title" dla istniejących transakcji',
+ 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions',
+ 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.',
'include_transactions_from_accounts' => 'Uwzględnij transakcje z tych kont',
+ 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.',
'execute' => 'Wykonaj',
+ 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions',
+ 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.',
+ 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.',
// actions and triggers
'rule_trigger_user_action' => 'Akcją użytkownika jest ":trigger_value"',
@@ -298,9 +300,9 @@ return [
'rule_trigger_description_ends_choice' => 'Opis kończy się na..',
'rule_trigger_description_contains_choice' => 'Opis zawiera..',
'rule_trigger_description_is_choice' => 'Opis jest..',
- 'rule_trigger_category_is_choice' => 'Category is..',
- 'rule_trigger_budget_is_choice' => 'Budget is..',
- 'rule_trigger_tag_is_choice' => '(A) tag is..',
+ 'rule_trigger_category_is_choice' => 'Kategoria jest..',
+ 'rule_trigger_budget_is_choice' => 'Budżet jest..',
+ 'rule_trigger_tag_is_choice' => 'Tag jest..',
'rule_trigger_has_attachments_choice' => 'Has at least this many attachments',
'rule_trigger_has_attachments' => 'Has at least :trigger_value attachment(s)',
'rule_trigger_store_journal' => 'Po utworzeniu transakcji',
@@ -426,12 +428,6 @@ return [
'attachment_updated' => 'Zmodyfikowano załącznik ":name"',
'upload_max_file_size' => 'Maksymalny rozmiar pliku to: :size',
- // tour:
- 'prev' => 'Poprz.',
- 'next' => 'Nast.',
- 'end-tour' => 'Koniec samouczka',
- 'pause' => 'Pauza',
-
// transaction index
'title_expenses' => 'Wydatki',
'title_withdrawal' => 'Wydatki',
@@ -499,15 +495,6 @@ return [
'make_default_currency' => 'ustaw domyślną',
'default_currency' => 'domyślna',
- // new user:
- 'submit' => 'Prześlij',
- 'getting_started' => 'Pierwsze kroki',
- 'to_get_started' => 'Aby rozpocząć pracę z Firefly, wprowadź nazwę twojego banku oraz saldo konta bankowego:',
- 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:',
- 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.',
- 'stored_new_account_new_user' => 'Yay! Twoje nowe konto zostało zachowane.',
- 'stored_new_accounts_new_user' => 'Yay! Twoje nowe konta zostały zapisane.',
-
// forms:
'mandatoryFields' => 'Pola wymagane',
'optionalFields' => 'Pola opcjonalne',
@@ -556,6 +543,8 @@ return [
'average_bill_amount_overall' => 'Średnia kwota rachunku (ogólnie)',
'not_or_not_yet' => 'Not (yet)',
'not_expected_period' => 'Not expected this period',
+ 'bill_is_active' => 'Bill is active',
+ 'bill_will_automatch' => 'Bill will automatically linked to matching transactions',
// accounts:
'details_for_asset' => 'Szczegóły konta aktywów ":name"',
'details_for_expense' => 'Szczegóły konta wydatków ":name"',
@@ -585,7 +574,7 @@ return [
'cash_accounts' => 'Konta gotówkowe',
'Cash account' => 'Konto gotówkowe',
'account_type' => 'Typ konta',
- 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:',
+ 'save_transactions_by_moving' => 'Zapisz te transakcje, przenosząc je do innego konta:',
'stored_new_account' => 'Nowe konto ":name" zostało zapisane!',
'updated_account' => 'Zaktualizowano konto ":name"',
'credit_card_options' => 'Opcje karty kredytowej',
@@ -594,9 +583,9 @@ return [
'select_more_than_one_account' => 'Proszę wybierz więcej niż jedno konto',
'select_more_than_one_category' => 'Proszę wybierz więcej niż jedną kategorię',
'select_more_than_one_budget' => 'Proszę wybierz więcej niż jeden budżet',
- 'select_more_than_one_tag' => 'Please select more than one tag',
+ 'select_more_than_one_tag' => 'Proszę wybierz więcej niż jeden tag',
'from_to' => 'Od :start do :end',
- 'from_to_breadcrumb' => 'from :start to :end',
+ 'from_to_breadcrumb' => 'od :start do :end',
'account_default_currency' => 'If you select another currency, new transactions from this account will have this currency pre-selected.',
// categories:
@@ -625,10 +614,10 @@ return [
'delete_withdrawal' => 'Usunięto wypłatę ":description"',
'delete_deposit' => 'Usuń wpłatę ":description"',
'delete_transfer' => 'Usuń transfer ":description"',
- 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"',
- 'deleted_deposit' => 'Successfully deleted deposit ":description"',
- 'deleted_transfer' => 'Successfully deleted transfer ":description"',
- 'stored_journal' => 'Successfully created new transaction ":description"',
+ 'deleted_withdrawal' => 'Pomyślnie usunięto wypłatę ":description"',
+ 'deleted_deposit' => 'Pomyślnie usunięto depozyt ":description"',
+ 'deleted_transfer' => 'Pomyślnie usunięto transfer ":description"',
+ 'stored_journal' => 'Pomyślnie utworzono nową transakcję ":description"',
'select_transactions' => 'Wybierz transakcje',
'stop_selection' => 'Stop selecting transactions',
'edit_selected' => 'Modyfikuj zaznaczone',
@@ -643,6 +632,12 @@ return [
// new user:
'welcome' => 'Witaj w Firefly!',
+ 'submit' => 'Prześlij',
+ 'getting_started' => 'Pierwsze kroki',
+ 'to_get_started' => 'It is good to see you have successfully installed Firefly III. To get started with this tool please enter your bank\'s name and the balance of your main checking account. Do not worry yet if you have multiple accounts. You can add those later. It\'s just that Firefly III needs something to start with.',
+ 'savings_balance_text' => 'Firefly III will automatically create a savings account for you. By default, there will be no money in your savings account, but if you tell Firefly III the balance it will be stored as such.',
+ 'finish_up_new_user' => 'That\'s it! You can continue by pressing Submit. You will be taken to the index of Firefly III.',
+ 'stored_new_accounts_new_user' => 'Yay! Twoje nowe konta zostały zapisane.',
// home page:
'yourAccounts' => 'Twoje konta',
@@ -755,12 +750,12 @@ return [
'report_type_audit' => 'Przegląd historii transakcji (audyt)',
'report_type_category' => 'Raport kategorii',
'report_type_budget' => 'Raport budżetów',
- 'report_type_tag' => 'Tag report',
+ 'report_type_tag' => 'Raport tagów',
'report_type_meta-history' => 'Przegląd kategorii, budżetów i rachunków',
'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.',
'report_included_accounts' => 'Uwzględnione konta',
'report_date_range' => 'Zakres dat',
- 'report_preset_ranges' => 'Pre-set ranges',
+ 'report_preset_ranges' => 'Predefiniowane zakresy',
'shared' => 'Udostępnione',
'fiscal_year' => 'Rok podatkowy',
'income_entry' => 'Income from account ":name" between :start and :end',
@@ -774,16 +769,16 @@ return [
'report_has_no_extra_options' => 'Ten raport nie ma dodatkowych opcji',
'reports_submit' => 'Zobacz raport',
'end_after_start_date' => 'Data zakończenia raportu musi być po dacie rozpoczęcia.',
- 'select_category' => 'Select category(ies)',
- 'select_budget' => 'Select budget(s).',
- 'select_tag' => 'Select tag(s).',
+ 'select_category' => 'Wybierz kategorię(e)',
+ 'select_budget' => 'Wybierz budżet(y).',
+ 'select_tag' => 'Wybierz tag(i).',
'income_per_category' => 'Dochód wg kategorii',
'expense_per_category' => 'Wydatek wg kategorii',
'expense_per_budget' => 'Wydatek wg budżetu',
'income_per_account' => 'Dochód wg konta',
'expense_per_account' => 'Wydatek wg konta',
- 'expense_per_tag' => 'Expense per tag',
- 'income_per_tag' => 'Income per tag',
+ 'expense_per_tag' => 'Wydatek wg tagu',
+ 'income_per_tag' => 'Przychód wg tagu',
'include_expense_not_in_budget' => 'Included expenses not in the selected budget(s)',
'include_expense_not_in_account' => 'Included expenses not in the selected account(s)',
'include_expense_not_in_category' => 'Included expenses not in the selected category(ies)',
@@ -882,7 +877,7 @@ return [
'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
'delete_tag' => 'Usuń tag ":tag"',
'deleted_tag' => 'Usunięto tag ":tag"',
- 'new_tag' => 'Make new tag',
+ 'new_tag' => 'Utwórz nowy tag',
'edit_tag' => 'Modyfikuj tag ":tag"',
'updated_tag' => 'Zaktualizowano tag ":tag"',
'created_tag' => 'Tag ":tag" został utworzony!',
@@ -896,7 +891,7 @@ return [
'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',
'transaction_journal_information' => 'Informacje o transakcji',
- 'transaction_journal_meta' => 'Meta information',
+ 'transaction_journal_meta' => 'Meta informacje',
'total_amount' => 'Łączna kwota',
'number_of_decimals' => 'Ilość miejsc dziesiętnych',
@@ -934,11 +929,11 @@ return [
// split a transaction:
'transaction_meta_data' => 'Transaction meta-data',
- 'transaction_dates' => 'Transaction dates',
+ 'transaction_dates' => 'Daty transakcji',
'splits' => 'Podziały',
'split_title_withdrawal' => 'Podziel swoją nową wypłatę',
- 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.',
- 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.',
+ 'split_intro_one_withdrawal' => 'Firefly obsługuje "dzielenie" wypłaty.',
+ 'split_intro_two_withdrawal' => 'Oznacza to, że ilość pieniędzy, które wydałeś jest podzielona między kilka kont wypłat, budżetów lub kategorii.',
'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.',
'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_withdrawal' => 'Zachowaj podzieloną wypłatę',
@@ -973,12 +968,12 @@ return [
// import index page:
'import_index_title' => 'Import data into Firefly III',
- 'import_index_sub_title' => 'Index',
+ 'import_index_sub_title' => 'Indeks',
'import_index_intro' => 'Welcome to Firefly\'s import routine. These pages can help you import data from your bank into Firefly III. Please check out the help pages in the top right corner.',
- 'import_index_file' => 'Select your file',
+ 'import_index_file' => 'Wybierz swój plik',
'import_index_config' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.',
'import_index_type' => 'Select the type of file you will upload',
- 'import_index_start' => 'Start importing',
+ 'import_index_start' => 'Rozpocznij import',
// supported file types:
'import_file_type_csv' => 'CSV (wartości oddzielone przecinkami)',
@@ -988,13 +983,13 @@ return [
'import_config_bread_crumb' => 'Set up your import file',
// import status page:
- 'import_status_bread_crumb' => 'Import status',
- 'import_status_sub_title' => 'Import status',
+ 'import_status_bread_crumb' => 'Status importu',
+ 'import_status_sub_title' => 'Status importu',
'import_status_wait_title' => 'Please hold...',
'import_status_wait_text' => 'This box will disappear in a moment.',
- 'import_status_ready_title' => 'Import is ready to start',
+ 'import_status_ready_title' => 'Import jest gotowy do uruchomienia',
'import_status_ready_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.',
- 'import_status_ready_config' => 'Download configuration',
+ 'import_status_ready_config' => 'Pobierz konfigurację',
'import_status_ready_start' => 'Start the import',
'import_status_ready_share' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.',
'import_status_running_title' => 'The import is running',
@@ -1011,57 +1006,57 @@ return [
'import_with_key' => 'Import z kluczem \':key\'',
// different states:
- 'import_status_job_running' => 'The import is underway. Please be patient...',
+ 'import_status_job_running' => 'Import jest w toku. Proszę o cierpliwość...',
// sandstorm.io errors and messages:
- 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.',
+ 'sandstorm_not_available' => 'Ta funkcja nie jest dostępna, gdy używasz Firefly III w środowisku Sandstorm.io.',
// empty lists? no objects? instructions:
- 'no_transactions_in_period' => 'There are no transactions in this period.',
- 'no_accounts_title_asset' => 'Let\'s create an asset account!',
- 'no_accounts_intro_asset' => 'You have no asset accounts yet. Asset accounts are your main accounts: your checking account, savings account, shared account or even your credit card.',
- 'no_accounts_imperative_asset' => 'To start using Firefly III you must create at least one asset account. Let\'s do so now:',
- 'no_accounts_create_asset' => 'Create an asset account',
- 'no_accounts_title_expense' => 'Let\'s create an expense account!',
+ 'no_transactions_in_period' => 'Nie ma żadnych transakcji w tym okresie.',
+ 'no_accounts_title_asset' => 'Stwórzmy konto aktywów!',
+ 'no_accounts_intro_asset' => 'Nie masz jeszcze konta nie aktywów. Konta aktywów są twoimi głównymi kontami: konto czekowe, konto oszczędnościowe, wspólne konto lub nawet karta kredytowa.',
+ 'no_accounts_imperative_asset' => 'Aby rozpocząć korzystanie z Firefly III należy utworzyć co najmniej jedno konto aktywów. Zróbmy je więc teraz:',
+ 'no_accounts_create_asset' => 'Utwórz konto aktywów',
+ 'no_accounts_title_expense' => 'Stwórzmy konto wydatków!',
'no_accounts_intro_expense' => 'You have no expense accounts yet. Expense accounts are the places where you spend money, such as shops and supermarkets.',
'no_accounts_imperative_expense' => 'Expense accounts are created automatically when you create transactions, but you can create one manually too, if you want. Let\'s create one now:',
- 'no_accounts_create_expense' => 'Create an expense account',
- 'no_accounts_title_revenue' => 'Let\'s create a revenue account!',
+ 'no_accounts_create_expense' => 'Utwórz konto wydatków',
+ 'no_accounts_title_revenue' => 'Stwórzmy konto przychodów!',
'no_accounts_intro_revenue' => 'You have no revenue accounts yet. Revenue accounts are the places where you receive money from, such as your employer.',
'no_accounts_imperative_revenue' => 'Revenue accounts are created automatically when you create transactions, but you can create one manually too, if you want. Let\'s create one now:',
- 'no_accounts_create_revenue' => 'Create a revenue account',
- 'no_budgets_title_default' => 'Let\'s create a budget',
+ 'no_accounts_create_revenue' => 'Utwórz konto przychodów',
+ 'no_budgets_title_default' => 'Stwórzmy budżet',
'no_budgets_intro_default' => 'You have no budgets yet. Budgets are used to organise your expenses into logical groups, which you can give a soft-cap to limit your expenses.',
'no_budgets_imperative_default' => 'Budgets are the basic tools of financial management. Let\'s create one now:',
- 'no_budgets_create_default' => 'Create a budget',
- 'no_categories_title_default' => 'Let\'s create a category!',
+ 'no_budgets_create_default' => 'Stwórz budżet',
+ 'no_categories_title_default' => 'Stwórzmy kategorię!',
'no_categories_intro_default' => 'You have no categories yet. Categories are used to fine tune your transactions and label them with their designated category.',
'no_categories_imperative_default' => 'Categories are created automatically when you create transactions, but you can create one manually too. Let\'s create one now:',
'no_categories_create_default' => 'Create a category',
- 'no_tags_title_default' => 'Let\'s create a tag!',
+ 'no_tags_title_default' => 'Stwórzmy tag!',
'no_tags_intro_default' => 'You have no tags yet. Tags are used to fine tune your transactions and label them with specific keywords.',
'no_tags_imperative_default' => 'Tags are created automatically when you create transactions, but you can create one manually too. Let\'s create one now:',
- 'no_tags_create_default' => 'Create a tag',
- 'no_transactions_title_withdrawal' => 'Let\'s create an expense!',
- 'no_transactions_intro_withdrawal' => 'You have no expenses yet. You should create expenses to start managing your finances.',
- 'no_transactions_imperative_withdrawal' => 'Have you spent some money? Then you should write it down:',
- 'no_transactions_create_withdrawal' => 'Create an expense',
- 'no_transactions_title_deposit' => 'Let\'s create some income!',
- 'no_transactions_intro_deposit' => 'You have no recorded income yet. You should create income entries to start managing your finances.',
- 'no_transactions_imperative_deposit' => 'Have you received some money? Then you should write it down:',
+ 'no_tags_create_default' => 'Utwórz tag',
+ 'no_transactions_title_withdrawal' => 'Stwórzmy wydatek!',
+ 'no_transactions_intro_withdrawal' => 'Jeszcze nie masz wydatków. Powinieneś utworzyć wydatki aby rozpocząć zarządzanie swoimi finansami.',
+ 'no_transactions_imperative_withdrawal' => 'Wydałeś trochę pieniędzy? Zatem powinieneś je zapisać:',
+ 'no_transactions_create_withdrawal' => 'Utwórz wydatek',
+ 'no_transactions_title_deposit' => 'Stwórzmy przychód!',
+ 'no_transactions_intro_deposit' => 'Jeszcze nie masz przychodów. Powinieneś utworzyć przychody aby rozpocząć zarządzanie swoimi finansami.',
+ 'no_transactions_imperative_deposit' => 'Otrzymałeś trochę pieniędzy? Zatem powinieneś je zapisać:',
'no_transactions_create_deposit' => 'Create a deposit',
- 'no_transactions_title_transfers' => 'Let\'s create a transfer!',
+ 'no_transactions_title_transfers' => 'Stwórzmy transfer!',
'no_transactions_intro_transfers' => 'You have no transfers yet. When you move money between asset accounts, it is recorded as a transfer.',
'no_transactions_imperative_transfers' => 'Have you moved some money around? Then you should write it down:',
- 'no_transactions_create_transfers' => 'Create a transfer',
- 'no_piggies_title_default' => 'Let\'s create a piggy bank!',
+ 'no_transactions_create_transfers' => 'Utwórz transfer',
+ 'no_piggies_title_default' => 'Stwórzmy skarbonkę!',
'no_piggies_intro_default' => 'You have no piggy banks yet. You can create piggy banks to divide your savings and keep track of what you\'re saving up for.',
'no_piggies_imperative_default' => 'Do you have things you\'re saving money for? Create a piggy bank and keep track:',
- 'no_piggies_create_default' => 'Create a new piggy bank',
- 'no_bills_title_default' => 'Let\'s create a bill!',
+ 'no_piggies_create_default' => 'Utwórz nową skarbonkę',
+ 'no_bills_title_default' => 'Stwórzmy rachunek!',
'no_bills_intro_default' => 'You have no bills yet. You can create bills to keep track of regular expenses, like your rent of insurance.',
'no_bills_imperative_default' => 'Do you have such regular bills? Create a bill and keep track of your payments:',
- 'no_bills_create_default' => 'Create a bill',
+ 'no_bills_create_default' => 'Utwórz rachunek',
];
\ No newline at end of file
diff --git a/resources/lang/pl_PL/form.php b/resources/lang/pl_PL/form.php
index 5d7cc9dbed..ddf76bd3a8 100644
--- a/resources/lang/pl_PL/form.php
+++ b/resources/lang/pl_PL/form.php
@@ -42,7 +42,7 @@ return [
'journal_description' => 'Opis',
'note' => 'Notatki',
'split_journal' => 'Podziel tę transakcję',
- 'split_journal_explanation' => 'Split this transaction in multiple parts',
+ 'split_journal_explanation' => 'Podziel transakcję na wiele części',
'currency' => 'Waluta',
'account_id' => 'Konto aktywów',
'budget_id' => 'Budżet',
diff --git a/resources/lang/pl_PL/intro.php b/resources/lang/pl_PL/intro.php
new file mode 100644
index 0000000000..7fb42114a8
--- /dev/null
+++ b/resources/lang/pl_PL/intro.php
@@ -0,0 +1,118 @@
+ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.',
+ 'index_accounts-chart' => 'This chart shows the current balance of your asset accounts. You can select the accounts visible here in your preferences.',
+ 'index_box_out_holder' => 'This little box and the boxes next to this one will give you a quick overview of your financial situation.',
+ 'index_help' => 'If you ever need help with a page or a form, press this button.',
+ 'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
+ 'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
+
+ // create account:
+ 'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
+ 'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly.',
+ 'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
+ 'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
+
+ // budgets index
+ 'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
+ 'budgets_index_set_budget' => 'Set your total budget for every period so Firefly can tell you if you have budgeted all available money.',
+ 'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
+ 'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
+ 'budgets_index_new_budget' => 'Create new budgets as you see fit.',
+ 'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
+
+ // reports (index)
+ 'reports_index_intro' => 'Use these reports to get detailed insights in your finances.',
+ 'reports_index_inputReportType' => 'Pick a report type. Check out the help pages to see what each report shows you.',
+ 'reports_index_inputAccountsSelect' => 'You can exclude or include asset accounts as you see fit.',
+ 'reports_index_inputDateRange' => 'The selected date range is entirely up to you: from one day to 10 years.',
+ 'reports_index_extra-options-box' => 'Depending on the report you have selected, you can select extra filters and options here. Watch this box when you change report types.',
+
+ // reports (reports)
+ 'reports_report_default_intro' => 'This report will give you a quick and comprehensive overview of your finances. If you wish to see anything else, please don\'t hestitate to contact me!',
+ 'reports_report_audit_intro' => 'This report will give you detailed insights in your asset accounts.',
+ 'reports_report_audit_optionsBox' => 'Use these check boxes to show or hide the columns you are interested in.',
+
+ 'reports_report_category_intro' => 'This report will give you insight in or multiple categories.',
+ 'reports_report_category_pieCharts' => 'These charts will give you insight in expenses and income per category or per account.',
+ 'reports_report_category_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per category.',
+
+ 'reports_report_tag_intro' => 'This report will give you insight in or multiple tags.',
+ 'reports_report_tag_pieCharts' => 'These charts will give you insight in expenses and income per tag, account, category or budget.',
+ 'reports_report_tag_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per tag.',
+
+ 'reports_report_budget_intro' => 'This report will give you insight in or multiple budgets.',
+ 'reports_report_budget_pieCharts' => 'These charts will give you insight in expenses and income per budget or per account.',
+ 'reports_report_budget_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per budget.',
+
+ // create transaction
+ 'transactions_create_switch_box' => 'Use these buttons to quickly switch the type of transaction you wish to save.',
+ 'transactions_create_ffInput_category' => 'You can freely type in this field. Previously created categories will be suggested.',
+ 'transactions_create_withdrawal_ffInput_budget' => 'Link your withdrawal to a budget for better financial control.',
+ 'transactions_create_withdrawal_currency_dropdown_amount' => 'Use this dropdown when your withdrawal is in another currency.',
+ 'transactions_create_deposit_currency_dropdown_amount' => 'Use this dropdown when your deposit is in another currency.',
+ 'transactions_create_transfer_ffInput_piggy_bank_id' => 'Select a piggy bank and link this transfer to your savings.',
+
+ // piggy banks index:
+ 'piggy-banks_index_saved' => 'This field shows you how much you\'ve saved in each piggy bank.',
+ 'piggy-banks_index_button' => 'Next to this progress bar are two buttons (+ and -) to add or remove money from each piggy bank.',
+ 'piggy-banks_index_accountStatus' => 'For each asset account with at least one piggy bank the status is listed in this table.',
+
+ // create piggy
+ 'piggy-banks_create_name' => 'What is your goal? A new couch, a camera, money for emergencies?',
+ 'piggy-banks_create_date' => 'You can set a target date or a deadline for your piggy bank.',
+
+ // show piggy
+ 'piggy-banks_show_piggyChart' => 'This chart will show the history of this piggy bank.',
+ 'piggy-banks_show_piggyDetails' => 'Some details about your piggy bank',
+ 'piggy-banks_show_piggyEvents' => 'Any additions or removals are also listed here.',
+
+ // bill index
+ 'bills_index_paid_in_period' => 'This field indicates when the bill was last paid.',
+ 'bills_index_expected_in_period' => 'This field indicates for each bill if and when the next bill is expected to hit.',
+
+ // show bill
+ 'bills_show_billInfo' => 'This table shows some general information about this bill.',
+ 'bills_show_billButtons' => 'Use this button to re-scan old transactions so they will be matched to this bill.',
+ 'bills_show_billChart' => 'This chart shows the transactions linked to this bill.',
+
+ // create bill
+ 'bills_create_name' => 'Use a descriptive name such as "Rent" or "Health insurance".',
+ 'bills_create_match' => 'To match transactions, use terms from those transactions or the expense account involved. All words must match.',
+ 'bills_create_amount_min_holder' => 'Select a minimum and maximum amount for this bill.',
+ 'bills_create_repeat_freq_holder' => 'Most bills repeat monthly, but you can set another frequency here.',
+ 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks for example, the "skip"-field should be set to "1" to skip every other week.',
+
+ // rules index
+ 'rules_index_intro' => 'Firefly III allows you to manage rules, that will automagically be applied to any transaction you create or edit.',
+ 'rules_index_new_rule_group' => 'You can combine rules in groups for easier management.',
+ 'rules_index_new_rule' => 'Create as many rules as you like.',
+ 'rules_index_prio_buttons' => 'Order them any way you see fit.',
+ 'rules_index_test_buttons' => 'You can test your rules or apply them to existing transactions.',
+ 'rules_index_rule-triggers' => 'Rules have "triggers" and "actions" that you can order by drag-and-drop.',
+ 'rules_index_outro' => 'Be sure to check out the help pages using the (?) icon in the top right!',
+
+ // create rule:
+ 'rules_create_mandatory' => 'Choose a descriptive title, and set when the rule should be fired.',
+ 'rules_create_ruletriggerholder' => 'Add as many triggers as you like, but remember that ALL triggers must match before any actions are fired.',
+ 'rules_create_test_rule_triggers' => 'Use this button to see which transactions would match your rule.',
+ 'rules_create_actions' => 'Set as many actions as you like.',
+
+ // preferences
+ 'preferences_index_tabs' => 'More options are available behind these tabs.',
+
+ // currencies
+ 'currencies_index_default' => 'Firefly III has one default currency. You can always switch of course using these buttons.',
+
+ // create currency
+ 'currencies_create_code' => 'This code should be ISO compliant (Google it for your new currency).',
+];
\ No newline at end of file
diff --git a/resources/lang/pt_BR/breadcrumbs.php b/resources/lang/pt_BR/breadcrumbs.php
index cd6c42b790..3fdbb55876 100644
--- a/resources/lang/pt_BR/breadcrumbs.php
+++ b/resources/lang/pt_BR/breadcrumbs.php
@@ -24,7 +24,7 @@ return [
'edit_bill' => 'Editar fatura ":name"',
'delete_bill' => 'Apagar fatura ":name"',
'reports' => 'Relatórios',
- 'searchResult' => 'Pesquisar por ":query"',
+ 'search_result' => 'Search results for ":query"',
'withdrawal_list' => 'Despesas',
'deposit_list' => 'Receitas, renda e depósitos',
'transfer_list' => 'Transferências',
diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php
index 99f7e8778f..d2cce1d67f 100644
--- a/resources/lang/pt_BR/firefly.php
+++ b/resources/lang/pt_BR/firefly.php
@@ -27,8 +27,7 @@ return [
'showEverything' => 'Mostrar tudo',
'never' => 'Nunca',
'search_results_for' => 'Pesquisar resultados por ":query"',
- 'advanced_search' => 'Busca avançada',
- 'advanced_search_intro' => 'Existem vários modificadores que você pode usar em sua busca para limitar os resultados. Se você usar qualquer um destes, a busca vai apenas transações de retorno. Por favor, clique no -ícone para obter mais informações.',
+ 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.',
'bounced_error' => 'A mensagem enviado para :email ressaltado, não tem acesso para você.',
'deleted_error' => 'Estas credenciais não correspondem aos nossos registros.',
'general_blocked_error' => 'Sua conta foi desativada, você não pode entrar.',
@@ -69,13 +68,6 @@ return [
'two_factor_lost_fix_owner' => 'Caso contrário, o proprietário do site :site_owner e peça para redefinir a sua autenticação de duas etapas.',
'warning_much_data' => ':days dias de dados podem demorar um pouco para carregar.',
'registered' => 'Você se registrou com sucesso!',
- 'search' => 'Pesquisa',
- 'search_found_accounts' => 'Encontrado: contar conta (s) para sua consulta.',
- 'search_found_categories' => 'Encontrado: contar categoria(s) para sua consulta.',
- 'search_found_budgets' => 'Encontrado: contagem despesa(s) para a sua consulta.',
- 'search_found_tags' => 'Encontrado: contar Tag (s) para sua consulta.',
- 'search_found_transactions' => 'Encontrado: contagem de transação para a sua consulta.',
- 'results_limited' => 'Os resultados são limitados a: contar as entradas.',
'tagbalancingAct' => 'Saldo',
'tagadvancePayment' => 'Pagamento antecipado',
'tagnothing' => '',
@@ -88,6 +80,8 @@ return [
'user_id_is' => 'Seu id de usuário é :user',
'field_supports_markdown' => 'This field supports Markdown.',
'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.',
+ 'reenable_intro_text' => 'You can also reenable the introduction guidance.',
+ 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.',
'nothing_to_display' => 'There are no transactions to show you',
'show_all_no_filter' => 'Show all transactions without grouping them by date.',
'expenses_by_category' => 'Despesa por categoria',
@@ -152,6 +146,15 @@ return [
'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.',
'transaction_data' => 'Transaction data',
+ // search
+ 'search' => 'Pesquisa',
+ 'search_found_transactions' => 'Number of transactions found:',
+ 'general_search_error' => 'An error occured while searching. Please check the log files for more information.',
+ 'search_box' => 'Search',
+ 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.',
+ 'search_error' => 'Error while searching',
+ 'search_searching' => 'Searching ...',
+
// repeat frequencies:
'repeat_freq_yearly' => 'anual',
'repeat_freq_monthly' => 'mensal',
@@ -204,7 +207,6 @@ return [
// rules
'rules' => 'Regras',
- 'rules_explanation' => 'Aqui você pode gerenciar as regras. As regras são acionadas quando uma transação é criada ou atualizada. Então, se a transação tiver certas propriedades (chamadas de "gatilhos") Firefly executará as "ações". Combinado, você pode fazer Firefly responder de uma certa maneira para novas transações.',
'rule_name' => 'Nome da regra',
'rule_triggers' => 'Regra dispara quando',
'rule_actions' => 'Regra será',
@@ -254,14 +256,14 @@ return [
'warning_transaction_subset' => 'Por razões de desempenho esta lista está limitado a :max_num_transactions e só pode mostrar um subconjunto das transações correspondentes',
'warning_no_matching_transactions' => 'Nenhuma transação correspondente encontrada. Por favor note que por motivos de desempenho, apenas as últimas :num_transactions transações tenham sido verificadas.',
'warning_no_valid_triggers' => 'Sem gatilhos válidos fornecidos.',
- 'execute_on_existing_transactions' => 'Executar transações existentes',
- 'rule_group_select_transactions' => 'Execute rule group ":title" on existing transactions',
- 'execute_on_existing_transactions_intro' => 'Quando uma regra ou um grupo for alterado ou adicionado, você pode executá-lo para transações existentes',
- 'execute_on_existing_transactions_short' => 'Transações existentes',
- 'executed_group_on_existing_transactions' => 'Executado o grupo ":title" para transações existentes',
- 'execute_group_on_existing_transactions' => 'Executar o grupo ":title" para transações existentes',
+ 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions',
+ 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.',
'include_transactions_from_accounts' => 'Incluir as transações destas contas',
+ 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.',
'execute' => 'Executar',
+ 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions',
+ 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.',
+ 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.',
// actions and triggers
'rule_trigger_user_action' => 'Ação do usuário é ":trigger_value"',
@@ -426,12 +428,6 @@ return [
'attachment_updated' => 'Anexo atualizado ":name"',
'upload_max_file_size' => 'Tamanho máximo do arquivo: :size',
- // tour:
- 'prev' => 'Anterior',
- 'next' => 'Próximo',
- 'end-tour' => 'Fim do Tour',
- 'pause' => 'Parar',
-
// transaction index
'title_expenses' => 'Despesas',
'title_withdrawal' => 'Despesas',
@@ -499,15 +495,6 @@ return [
'make_default_currency' => 'tornar padrão',
'default_currency' => 'padrão',
- // new user:
- 'submit' => 'Enviar',
- 'getting_started' => 'Iniciar',
- 'to_get_started' => 'Para começar com o Firefly, por favor digite o nome do seu banco atual e o saldo de sua conta corrente:',
- 'savings_balance_text' => 'Se você tem uma conta poupança, por favor, digite o saldo atual de sua conta de poupança:',
- 'cc_balance_text' => 'Se você tiver um cartão de crédito, por favor digite o limite do seu cartão de crédito.',
- 'stored_new_account_new_user' => 'Yay! Sua nova conta foi armazenada.',
- 'stored_new_accounts_new_user' => 'Yay! Suas novas contas foram armazenadas.',
-
// forms:
'mandatoryFields' => 'Campos obrigatórios',
'optionalFields' => 'Campos opcionais',
@@ -556,6 +543,8 @@ return [
'average_bill_amount_overall' => 'Média de fatura (geral)',
'not_or_not_yet' => 'Não (ainda)',
'not_expected_period' => 'Não esperado neste período',
+ 'bill_is_active' => 'Bill is active',
+ 'bill_will_automatch' => 'Bill will automatically linked to matching transactions',
// accounts:
'details_for_asset' => 'Detalhes para a conta de ativo ":name"',
'details_for_expense' => 'Detalhes para a conta de despesas ":name"',
@@ -643,6 +632,12 @@ return [
// new user:
'welcome' => 'Bem Vindo ao Firefly!',
+ 'submit' => 'Enviar',
+ 'getting_started' => 'Iniciar',
+ 'to_get_started' => 'It is good to see you have successfully installed Firefly III. To get started with this tool please enter your bank\'s name and the balance of your main checking account. Do not worry yet if you have multiple accounts. You can add those later. It\'s just that Firefly III needs something to start with.',
+ 'savings_balance_text' => 'Firefly III will automatically create a savings account for you. By default, there will be no money in your savings account, but if you tell Firefly III the balance it will be stored as such.',
+ 'finish_up_new_user' => 'That\'s it! You can continue by pressing Submit. You will be taken to the index of Firefly III.',
+ 'stored_new_accounts_new_user' => 'Yay! Suas novas contas foram armazenadas.',
// home page:
'yourAccounts' => 'Suas contas',
diff --git a/resources/lang/pt_BR/intro.php b/resources/lang/pt_BR/intro.php
new file mode 100644
index 0000000000..7fb42114a8
--- /dev/null
+++ b/resources/lang/pt_BR/intro.php
@@ -0,0 +1,118 @@
+ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.',
+ 'index_accounts-chart' => 'This chart shows the current balance of your asset accounts. You can select the accounts visible here in your preferences.',
+ 'index_box_out_holder' => 'This little box and the boxes next to this one will give you a quick overview of your financial situation.',
+ 'index_help' => 'If you ever need help with a page or a form, press this button.',
+ 'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
+ 'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
+
+ // create account:
+ 'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
+ 'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly.',
+ 'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
+ 'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
+
+ // budgets index
+ 'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
+ 'budgets_index_set_budget' => 'Set your total budget for every period so Firefly can tell you if you have budgeted all available money.',
+ 'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
+ 'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
+ 'budgets_index_new_budget' => 'Create new budgets as you see fit.',
+ 'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
+
+ // reports (index)
+ 'reports_index_intro' => 'Use these reports to get detailed insights in your finances.',
+ 'reports_index_inputReportType' => 'Pick a report type. Check out the help pages to see what each report shows you.',
+ 'reports_index_inputAccountsSelect' => 'You can exclude or include asset accounts as you see fit.',
+ 'reports_index_inputDateRange' => 'The selected date range is entirely up to you: from one day to 10 years.',
+ 'reports_index_extra-options-box' => 'Depending on the report you have selected, you can select extra filters and options here. Watch this box when you change report types.',
+
+ // reports (reports)
+ 'reports_report_default_intro' => 'This report will give you a quick and comprehensive overview of your finances. If you wish to see anything else, please don\'t hestitate to contact me!',
+ 'reports_report_audit_intro' => 'This report will give you detailed insights in your asset accounts.',
+ 'reports_report_audit_optionsBox' => 'Use these check boxes to show or hide the columns you are interested in.',
+
+ 'reports_report_category_intro' => 'This report will give you insight in or multiple categories.',
+ 'reports_report_category_pieCharts' => 'These charts will give you insight in expenses and income per category or per account.',
+ 'reports_report_category_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per category.',
+
+ 'reports_report_tag_intro' => 'This report will give you insight in or multiple tags.',
+ 'reports_report_tag_pieCharts' => 'These charts will give you insight in expenses and income per tag, account, category or budget.',
+ 'reports_report_tag_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per tag.',
+
+ 'reports_report_budget_intro' => 'This report will give you insight in or multiple budgets.',
+ 'reports_report_budget_pieCharts' => 'These charts will give you insight in expenses and income per budget or per account.',
+ 'reports_report_budget_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per budget.',
+
+ // create transaction
+ 'transactions_create_switch_box' => 'Use these buttons to quickly switch the type of transaction you wish to save.',
+ 'transactions_create_ffInput_category' => 'You can freely type in this field. Previously created categories will be suggested.',
+ 'transactions_create_withdrawal_ffInput_budget' => 'Link your withdrawal to a budget for better financial control.',
+ 'transactions_create_withdrawal_currency_dropdown_amount' => 'Use this dropdown when your withdrawal is in another currency.',
+ 'transactions_create_deposit_currency_dropdown_amount' => 'Use this dropdown when your deposit is in another currency.',
+ 'transactions_create_transfer_ffInput_piggy_bank_id' => 'Select a piggy bank and link this transfer to your savings.',
+
+ // piggy banks index:
+ 'piggy-banks_index_saved' => 'This field shows you how much you\'ve saved in each piggy bank.',
+ 'piggy-banks_index_button' => 'Next to this progress bar are two buttons (+ and -) to add or remove money from each piggy bank.',
+ 'piggy-banks_index_accountStatus' => 'For each asset account with at least one piggy bank the status is listed in this table.',
+
+ // create piggy
+ 'piggy-banks_create_name' => 'What is your goal? A new couch, a camera, money for emergencies?',
+ 'piggy-banks_create_date' => 'You can set a target date or a deadline for your piggy bank.',
+
+ // show piggy
+ 'piggy-banks_show_piggyChart' => 'This chart will show the history of this piggy bank.',
+ 'piggy-banks_show_piggyDetails' => 'Some details about your piggy bank',
+ 'piggy-banks_show_piggyEvents' => 'Any additions or removals are also listed here.',
+
+ // bill index
+ 'bills_index_paid_in_period' => 'This field indicates when the bill was last paid.',
+ 'bills_index_expected_in_period' => 'This field indicates for each bill if and when the next bill is expected to hit.',
+
+ // show bill
+ 'bills_show_billInfo' => 'This table shows some general information about this bill.',
+ 'bills_show_billButtons' => 'Use this button to re-scan old transactions so they will be matched to this bill.',
+ 'bills_show_billChart' => 'This chart shows the transactions linked to this bill.',
+
+ // create bill
+ 'bills_create_name' => 'Use a descriptive name such as "Rent" or "Health insurance".',
+ 'bills_create_match' => 'To match transactions, use terms from those transactions or the expense account involved. All words must match.',
+ 'bills_create_amount_min_holder' => 'Select a minimum and maximum amount for this bill.',
+ 'bills_create_repeat_freq_holder' => 'Most bills repeat monthly, but you can set another frequency here.',
+ 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks for example, the "skip"-field should be set to "1" to skip every other week.',
+
+ // rules index
+ 'rules_index_intro' => 'Firefly III allows you to manage rules, that will automagically be applied to any transaction you create or edit.',
+ 'rules_index_new_rule_group' => 'You can combine rules in groups for easier management.',
+ 'rules_index_new_rule' => 'Create as many rules as you like.',
+ 'rules_index_prio_buttons' => 'Order them any way you see fit.',
+ 'rules_index_test_buttons' => 'You can test your rules or apply them to existing transactions.',
+ 'rules_index_rule-triggers' => 'Rules have "triggers" and "actions" that you can order by drag-and-drop.',
+ 'rules_index_outro' => 'Be sure to check out the help pages using the (?) icon in the top right!',
+
+ // create rule:
+ 'rules_create_mandatory' => 'Choose a descriptive title, and set when the rule should be fired.',
+ 'rules_create_ruletriggerholder' => 'Add as many triggers as you like, but remember that ALL triggers must match before any actions are fired.',
+ 'rules_create_test_rule_triggers' => 'Use this button to see which transactions would match your rule.',
+ 'rules_create_actions' => 'Set as many actions as you like.',
+
+ // preferences
+ 'preferences_index_tabs' => 'More options are available behind these tabs.',
+
+ // currencies
+ 'currencies_index_default' => 'Firefly III has one default currency. You can always switch of course using these buttons.',
+
+ // create currency
+ 'currencies_create_code' => 'This code should be ISO compliant (Google it for your new currency).',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/auth.php b/resources/lang/ru_RU/auth.php
new file mode 100644
index 0000000000..d4cc103d81
--- /dev/null
+++ b/resources/lang/ru_RU/auth.php
@@ -0,0 +1,28 @@
+ 'Неправильный адрес электронной почты или пароль.',
+ 'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте снова через :seconds секунд.',
+
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/breadcrumbs.php b/resources/lang/ru_RU/breadcrumbs.php
new file mode 100644
index 0000000000..22c5d21b8e
--- /dev/null
+++ b/resources/lang/ru_RU/breadcrumbs.php
@@ -0,0 +1,41 @@
+ 'Главная',
+ 'edit_currency' => 'Редактирование валюты ":name"',
+ 'delete_currency' => 'Удаление валюты ":name"',
+ 'newPiggyBank' => 'Создание новой копилки',
+ 'edit_piggyBank' => 'Редактирование копилки ":name"',
+ 'preferences' => 'Настройки',
+ 'profile' => 'Профиль',
+ 'changePassword' => 'Изменение вашего пароля',
+ 'bills' => 'Счета к оплате',
+ 'newBill' => 'Новый счёт к оплате',
+ 'edit_bill' => 'Редактирование счёта к оплате ":name"',
+ 'delete_bill' => 'Удаление счёта к оплате ":name"',
+ 'reports' => 'Отчёты',
+ 'search_result' => 'Search results for ":query"',
+ 'withdrawal_list' => 'Мои расходы',
+ 'deposit_list' => 'Мои доходы',
+ 'transfer_list' => 'Переводы',
+ 'transfers_list' => 'Переводы',
+ 'create_withdrawal' => 'Создать новый расход',
+ 'create_deposit' => 'Создать новый доход',
+ 'create_transfer' => 'Создать новый перевод',
+ 'edit_journal' => 'Редактирование транзакции ":description"',
+ 'delete_journal' => 'Удаление транзакции ":description"',
+ 'tags' => 'Метки',
+ 'createTag' => 'Создать новую метку',
+ 'edit_tag' => 'Редактирование метки ":tag"',
+ 'delete_tag' => 'Удаление метки ":tag"',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/config.php b/resources/lang/ru_RU/config.php
new file mode 100644
index 0000000000..4e7ce17f0f
--- /dev/null
+++ b/resources/lang/ru_RU/config.php
@@ -0,0 +1,23 @@
+ 'ru, Russian, ru_RU, ru_RU.utf8, ru_RU.UTF-8',
+ 'month' => '%B %Y',
+ 'month_and_day' => '%B %e, %Y',
+ 'date_time' => '%B %e, %Y, @ %T',
+ 'specific_day' => '%e %B %Y',
+ 'week_in_year' => 'Неделя %W, %Y',
+ 'quarter_of_year' => '%B %Y',
+ 'year' => '%Y',
+ 'half_year' => '%B %Y',
+
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/csv.php b/resources/lang/ru_RU/csv.php
new file mode 100644
index 0000000000..b02e2a226b
--- /dev/null
+++ b/resources/lang/ru_RU/csv.php
@@ -0,0 +1,81 @@
+ 'Импорт данных (1/3) - Подготовка к импорту CSV',
+ 'initial_text' => 'Чтобы импорт данных прошёл успешно, пожалуйста проверьте несколько параметров.',
+ 'initial_box' => 'Основные параметры импорта CSV',
+ 'initial_header_help' => 'Установите этот флажок, если первая строка CSV-файла содержит заголовки столбцов.',
+ 'initial_date_help' => 'Формат даты и времени в вашем CSV-файле. Придерживайтесь формата, описанного на этой странице. По умолчанию дату будут анализироваться на соответствие такому формату: :dateExample.',
+ 'initial_delimiter_help' => 'Выберите разделитель полей, который используется в вашем файле. Если вы не уверены, помните, что запятая - это самый безопасный вариант.',
+ 'initial_import_account_help' => 'Если ваш CSV-файл НЕ СОДЕРЖИТ информацию о ваших счётах, укажите счета для всех транзакций, выбрав подходящие из выпадающего списка.',
+ 'initial_submit' => 'Перейти к шагу 2/3',
+
+ // roles config
+ 'roles_title' => 'Import setup (2/3) - Define each column\'s role',
+ 'roles_text' => 'Each column in your CSV file contains certain data. Please indicate what kind of data the importer should expect. The option to "map" data means that you will link each entry found in the column to a value in your database. An often mapped column is the column that contains the IBAN of the opposing account. That can be easily matched to IBAN\'s present in your database already.',
+ 'roles_table' => 'Table',
+ 'roles_column_name' => 'Name of column',
+ 'roles_column_example' => 'Column example data',
+ 'roles_column_role' => 'Column data meaning',
+ 'roles_do_map_value' => 'Map these values',
+ 'roles_column' => 'Column',
+ 'roles_no_example_data' => 'No example data available',
+ 'roles_submit' => 'Continue with step 3/3',
+
+ // map data
+ 'map_title' => 'Import setup (3/3) - Connect import data to Firefly III data',
+ 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.',
+ 'map_field_value' => 'Field value',
+ 'map_field_mapped_to' => 'Mapped to',
+ 'map_do_not_map' => '(do not map)',
+ 'map_submit' => 'Start the import',
+
+ // map things.
+ 'column__ignore' => '(ignore this column)',
+ 'column_account-iban' => 'Asset account (IBAN)',
+ 'column_account-id' => 'Asset account ID (matching Firefly)',
+ 'column_account-name' => 'Asset account (name)',
+ 'column_amount' => 'Amount',
+ 'column_amount-comma-separated' => 'Amount (comma as decimal separator)',
+ 'column_bill-id' => 'Bill ID (matching Firefly)',
+ 'column_bill-name' => 'Bill name',
+ 'column_budget-id' => 'Budget ID (matching Firefly)',
+ 'column_budget-name' => 'Budget name',
+ 'column_category-id' => 'Category ID (matching Firefly)',
+ 'column_category-name' => 'Category name',
+ 'column_currency-code' => 'Currency code (ISO 4217)',
+ 'column_currency-id' => 'Currency ID (matching Firefly)',
+ 'column_currency-name' => 'Currency name (matching Firefly)',
+ 'column_currency-symbol' => 'Currency symbol (matching Firefly)',
+ 'column_date-interest' => 'Interest calculation date',
+ 'column_date-book' => 'Transaction booking date',
+ 'column_date-process' => 'Transaction process date',
+ 'column_date-transaction' => 'Date',
+ 'column_description' => 'Description',
+ 'column_opposing-iban' => 'Opposing account (IBAN)',
+ 'column_opposing-id' => 'Opposing account ID (matching Firefly)',
+ 'column_external-id' => 'External ID',
+ 'column_opposing-name' => 'Opposing account (name)',
+ 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator',
+ 'column_ing-debet-credit' => 'ING specific debet/credit indicator',
+ 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID',
+ 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account',
+ 'column_sepa-db' => 'SEPA Direct Debet',
+ 'column_tags-comma' => 'Tags (comma separated)',
+ 'column_tags-space' => 'Tags (space separated)',
+ 'column_account-number' => 'Asset account (account number)',
+ 'column_opposing-number' => 'Opposing account (account number)',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/demo.php b/resources/lang/ru_RU/demo.php
new file mode 100644
index 0000000000..41fdb645cf
--- /dev/null
+++ b/resources/lang/ru_RU/demo.php
@@ -0,0 +1,24 @@
+ 'Sorry, there is no extra demo-explanation text for this page.',
+ 'see_help_icon' => 'However, the -icon in the top right corner may tell you more.',
+ 'index' => 'Добро пожаловать в Firefly III! На этой странице вы видите вашу финансовую ситуацию в общих чертах. Более подробная информация доступна на страницах → Активные счета Бюджет и Отчёты. Или просто внимательно оглядитесь и изучите всё вокруг.',
+ 'accounts-index' => 'Asset accounts are your personal bank accounts. Expense accounts are the accounts you spend money at, such as stores and friends. Revenue accounts are accounts you receive money from, such as your job, the government or other sources of income. On this page you can edit or remove them.',
+ 'budgets-index' => 'This page shows you an overview of your budgets. The top bar shows the amount that is available to be budgeted. This can be customized for any period by clicking the amount on the right. The amount you\'ve actually spent is shown in the bar below. Below that are the expenses per budget and what you\'ve budgeted for them.',
+ 'reports-index-start' => 'Firefly III supports four types of reports. Read about them by clicking on the -icon in the top right corner.',
+ 'reports-index-examples' => 'Be sure to check out these examples: a monthly financial overview, a yearly financial overview and a budget overview.',
+ 'currencies-index' => 'Firefly III supports multiple currencies. Although it defaults to the Euro it can be set to the US Dollar and many other currencies. As you can see a small selection of currencies has been included but you can add your own if you wish to. Changing the default currency will not change the currency of existing transactions however: Firefly III supports the use of multiple currencies at the same time.',
+ 'transactions-index' => 'These expenses, deposits and transfers are not particularly imaginative. They have been generated automatically.',
+ 'piggy-banks-index' => 'As you can see, there are three piggy banks. Use the plus and minus buttons to influence the amount of money in each piggy bank. Click the name of the piggy bank to see the administration for each piggy bank.',
+ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ',
+ 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.',
+ 'import-configure-configuration' => 'The configuration you see below is correct for the local file.',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/firefly.php b/resources/lang/ru_RU/firefly.php
new file mode 100644
index 0000000000..c0de54bff1
--- /dev/null
+++ b/resources/lang/ru_RU/firefly.php
@@ -0,0 +1,1062 @@
+ 'незавершённый перевод',
+ 'close' => 'Закрыть',
+ 'actions' => 'Действия',
+ 'edit' => 'Изменить',
+ 'delete' => 'Удалить',
+ 'welcomeBack' => 'Что происходит с моими финансами?',
+ 'everything' => 'Everything',
+ 'customRange' => 'Custom range',
+ 'apply' => 'Применить',
+ 'select_date' => 'Выбрать дату...',
+ 'cancel' => 'Отмена',
+ 'from' => 'From',
+ 'to' => 'To',
+ 'showEverything' => 'Показать всё',
+ 'never' => 'Никогда',
+ 'search_results_for' => 'Search results for ":query"',
+ 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.',
+ 'bounced_error' => 'The message sent to :email bounced, so no access for you.',
+ 'deleted_error' => 'These credentials do not match our records.',
+ 'general_blocked_error' => 'Your account has been disabled, so you cannot login.',
+ 'expired_error' => 'Your account has expired, and can no longer be used.',
+ 'removed_amount' => 'Removed :amount',
+ 'added_amount' => 'Added :amount',
+ 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.',
+ 'Opening balance' => 'Opening balance',
+ 'create_new_stuff' => 'Create new stuff',
+ 'new_withdrawal' => 'New withdrawal',
+ 'new_deposit' => 'New deposit',
+ 'new_transfer' => 'New transfer',
+ 'new_asset_account' => 'New asset account',
+ 'new_expense_account' => 'New expense account',
+ 'new_revenue_account' => 'New revenue account',
+ 'new_budget' => 'New budget',
+ 'new_bill' => 'New bill',
+ 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?',
+ 'flash_success' => 'Успешно!',
+ 'flash_info' => 'Сообщение',
+ 'flash_warning' => 'Предупреждение!',
+ 'flash_error' => 'Ошибка!',
+ 'flash_info_multiple' => 'There is one message|There are :count messages',
+ 'flash_error_multiple' => 'There is one error|There are :count errors',
+ 'net_worth' => 'Net worth',
+ 'route_has_no_help' => 'There is no help for this route.',
+ 'help_may_not_be_your_language' => 'This help text is in English. It is not yet available in your language',
+ 'two_factor_welcome' => 'Hello, :user!',
+ 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.',
+ 'two_factor_code_here' => 'Enter code here',
+ 'two_factor_title' => 'Two factor authentication',
+ 'authenticate' => 'Authenticate',
+ 'two_factor_forgot_title' => 'Lost two factor authentication',
+ 'two_factor_forgot' => 'I forgot my two-factor thing.',
+ 'two_factor_lost_header' => 'Lost your two factor authentication?',
+ 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.',
+ 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs
for instructions.',
+ 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.',
+ 'warning_much_data' => ':days days of data may take a while to load.',
+ 'registered' => 'You have registered successfully!',
+ 'tagbalancingAct' => 'Balancing act',
+ 'tagadvancePayment' => 'Advance payment',
+ 'tagnothing' => '',
+ 'Default asset account' => 'Счёт по умолчанию',
+ 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.',
+ 'Savings account' => 'Savings account',
+ 'Credit card' => 'Credit card',
+ 'source_accounts' => 'Source account(s)',
+ 'destination_accounts' => 'Destination account(s)',
+ 'user_id_is' => 'Ваш id пользователя :user',
+ 'field_supports_markdown' => 'This field supports Markdown.',
+ 'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.',
+ 'reenable_intro_text' => 'You can also reenable the introduction guidance.',
+ 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.',
+ 'nothing_to_display' => 'There are no transactions to show you',
+ 'show_all_no_filter' => 'Показать все транзакции без группировки по датам.',
+ 'expenses_by_category' => 'Expenses by category',
+ 'expenses_by_budget' => 'Expenses by budget',
+ 'income_by_category' => 'Income by category',
+ 'expenses_by_asset_account' => 'Expenses by asset account',
+ 'expenses_by_expense_account' => 'Expenses by expense account',
+ 'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.',
+ 'sum_of_expenses' => 'Sum of expenses',
+ 'sum_of_income' => 'Sum of income',
+ 'total_sum' => 'Total sum',
+ 'spent_in_specific_budget' => 'Spent in budget ":budget"',
+ 'sum_of_expenses_in_budget' => 'Spent total in budget ":budget"',
+ 'left_in_budget_limit' => 'Left to spend according to budgeting',
+ 'cannot_change_demo' => 'You cannot change the password of the demonstration account.',
+ 'cannot_delete_demo' => 'You cannot remove the demonstration account.',
+ 'cannot_reset_demo_user' => 'You cannot reset the password of the demonstration account',
+ 'per_period' => 'Per period',
+ 'all_periods' => 'All periods',
+ 'current_period' => 'Current period',
+ 'show_the_current_period_and_overview' => 'Show the current period and overview',
+ 'pref_languages_locale' => 'Для корректной работы с языками, отличными от английского, ваша операционная система должна отдавать корректную информацию о локали. Если это не так, валюты, даты и суммы могут отображаться некорректно.',
+ 'budget_in_period' => 'All transactions for budget ":name" between :start and :end',
+ 'chart_budget_in_period' => 'Chart for all transactions for budget ":name" between :start and :end',
+ 'chart_account_in_period' => 'Chart for all transactions for account ":name" between :start and :end',
+ 'chart_category_in_period' => 'Chart for all transactions for category ":name" between :start and :end',
+ 'chart_category_all' => 'Chart for all transactions for category ":name"',
+ 'budget_in_period_breadcrumb' => 'Между :start и :end',
+ 'clone_withdrawal' => 'Clone this withdrawal',
+ 'clone_deposit' => 'Clone this deposit',
+ 'clone_transfer' => 'Clone this transfer',
+ 'transaction_journal_other_options' => 'Other options',
+ 'multi_select_no_selection' => 'None selected',
+ 'multi_select_all_selected' => 'All selected',
+ 'multi_select_filter_placeholder' => 'Find..',
+ 'between_dates_breadcrumb' => 'Между :start и :end',
+ 'all_journals_without_budget' => 'All transactions without a budget',
+ 'journals_without_budget' => 'Transactions without a budget',
+ 'all_journals_without_category' => 'All transactions without a category',
+ 'journals_without_category' => 'Transactions without a category',
+ 'all_journals_for_account' => 'All transactions for account :name',
+ 'chart_all_journals_for_account' => 'Chart of all transactions for account :name',
+ 'journals_in_period_for_account' => 'All transactions for account :name between :start and :end',
+ 'transferred' => 'Transferred',
+ 'all_withdrawal' => 'All expenses',
+ 'all_transactions' => 'All transactions',
+ 'title_withdrawal_between' => 'All expenses between :start and :end',
+ 'all_deposit' => 'Все доходы',
+ 'title_deposit_between' => 'Все доходы между :start и :end',
+ 'all_transfers' => 'Все переводы',
+ 'title_transfers_between' => 'Все переводы между :start и :end',
+ 'all_transfer' => 'Все переводы',
+ 'all_journals_for_tag' => 'All transactions for tag ":tag"',
+ 'title_transfer_between' => 'All transfers between :start and :end',
+ 'all_journals_for_category' => 'All transactions for category :name',
+ 'all_journals_for_budget' => 'All transactions for budget :name',
+ 'chart_all_journals_for_budget' => 'Chart of all transactions for budget :name',
+ 'journals_in_period_for_category' => 'All transactions for category :name between :start and :end',
+ 'journals_in_period_for_tag' => 'All transactions for tag :tag between :start and :end',
+ 'not_available_demo_user' => 'The feature you try to access is not available to demo users.',
+ 'exchange_rate_instructions' => 'Asset account "@name" only accepts transactions in @native_currency. If you wish to use @foreign_currency instead, make sure that the amount in @native_currency is known as well:',
+ 'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.',
+ 'transaction_data' => 'Transaction data',
+
+ // search
+ 'search' => 'Search',
+ 'search_found_transactions' => 'Number of transactions found:',
+ 'general_search_error' => 'An error occured while searching. Please check the log files for more information.',
+ 'search_box' => 'Search',
+ 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.',
+ 'search_error' => 'Error while searching',
+ 'search_searching' => 'Searching ...',
+
+ // repeat frequencies:
+ 'repeat_freq_yearly' => 'ежегодно',
+ 'repeat_freq_monthly' => 'ежемесячно',
+ 'weekly' => 'еженедельно',
+ 'quarterly' => 'раз в квартал',
+ 'half-year' => 'раз в полгода',
+ 'yearly' => 'ежегодно',
+ // account confirmation:
+ 'confirm_account_header' => 'Please confirm your account',
+ 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.',
+ 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.',
+ 'account_is_confirmed' => 'Your account has been confirmed!',
+ 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.',
+ 'confirm_account_is_resent_header' => 'The confirmation has been resent',
+ 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.',
+ 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly',
+ 'confirm_account_not_resent_header' => 'Something went wrong :(',
+ 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.',
+ 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly',
+
+ // export data:
+ 'import_and_export' => 'Импорт и экспорт',
+ 'export_data' => 'Экспорт данных',
+ 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.',
+ 'export_format' => 'Формат для экспорта',
+ 'export_format_csv' => 'Comma separated values (CSV file)',
+ 'export_format_mt940' => 'MT940 compatible format',
+ 'export_included_accounts' => 'Export transactions from these accounts',
+ 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.',
+ 'do_export' => 'Export',
+ 'export_status_never_started' => 'The export has not started yet',
+ 'export_status_make_exporter' => 'Creating exporter thing...',
+ 'export_status_collecting_journals' => 'Collecting your transactions...',
+ 'export_status_collected_journals' => 'Collected your transactions!',
+ 'export_status_converting_to_export_format' => 'Converting your transactions...',
+ 'export_status_converted_to_export_format' => 'Converted your transactions!',
+ 'export_status_creating_journal_file' => 'Creating the export file...',
+ 'export_status_created_journal_file' => 'Created the export file!',
+ 'export_status_collecting_attachments' => 'Collecting all your attachments...',
+ 'export_status_collected_attachments' => 'Collected all your attachments!',
+ 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...',
+ 'export_status_collected_old_uploads' => 'Collected all your previous uploads!',
+ 'export_status_creating_config_file' => 'Creating a configuration file...',
+ 'export_status_created_config_file' => 'Created a configuration file!',
+ 'export_status_creating_zip_file' => 'Creating a zip file...',
+ 'export_status_created_zip_file' => 'Created a zip file!',
+ 'export_status_finished' => 'Export has succesfully finished! Yay!',
+ 'export_data_please_wait' => 'Please wait...',
+ 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.',
+
+ // rules
+ 'rules' => 'Rules',
+ 'rule_name' => 'Name of rule',
+ 'rule_triggers' => 'Rule triggers when',
+ 'rule_actions' => 'Rule will',
+ 'new_rule' => 'New rule',
+ 'new_rule_group' => 'New rule group',
+ 'rule_priority_up' => 'Give rule more priority',
+ 'rule_priority_down' => 'Give rule less priority',
+ 'make_new_rule_group' => 'Make new rule group',
+ 'store_new_rule_group' => 'Store new rule group',
+ 'created_new_rule_group' => 'New rule group ":title" stored!',
+ 'updated_rule_group' => 'Successfully updated rule group ":title".',
+ 'edit_rule_group' => 'Edit rule group ":title"',
+ 'delete_rule_group' => 'Delete rule group ":title"',
+ 'deleted_rule_group' => 'Deleted rule group ":title"',
+ 'update_rule_group' => 'Update rule group',
+ 'no_rules_in_group' => 'There are no rules in this group',
+ 'move_rule_group_up' => 'Move rule group up',
+ 'move_rule_group_down' => 'Move rule group down',
+ 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:',
+ 'make_new_rule' => 'Make new rule in rule group ":title"',
+ 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.',
+ 'rule_help_active' => 'Inactive rules will never fire.',
+ 'stored_new_rule' => 'Stored new rule with title ":title"',
+ 'deleted_rule' => 'Deleted rule with title ":title"',
+ 'store_new_rule' => 'Store new rule',
+ 'updated_rule' => 'Updated rule with title ":title"',
+ 'default_rule_group_name' => 'Default rules',
+ 'default_rule_group_description' => 'All your rules not in a particular group.',
+ 'default_rule_name' => 'Your first default rule',
+ 'default_rule_description' => 'This rule is an example. You can safely delete it.',
+ 'default_rule_trigger_description' => 'The Man Who Sold the World',
+ 'default_rule_trigger_from_account' => 'David Bowie',
+ 'default_rule_action_prepend' => 'Bought the world from ',
+ 'default_rule_action_set_category' => 'Large expenses',
+ 'trigger' => 'Trigger',
+ 'trigger_value' => 'Trigger on value',
+ 'stop_processing_other_triggers' => 'Stop processing other triggers',
+ 'add_rule_trigger' => 'Add new trigger',
+ 'action' => 'Action',
+ 'action_value' => 'Action value',
+ 'stop_executing_other_actions' => 'Stop executing other actions',
+ 'add_rule_action' => 'Add new action',
+ 'edit_rule' => 'Edit rule ":title"',
+ 'delete_rule' => 'Delete rule ":title"',
+ 'update_rule' => 'Update rule',
+ 'test_rule_triggers' => 'See matching transactions',
+ 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions',
+ 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.',
+ 'warning_no_valid_triggers' => 'No valid triggers provided.',
+ 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions',
+ 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.',
+ 'include_transactions_from_accounts' => 'Include transactions from these accounts',
+ 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.',
+ 'execute' => 'Execute',
+ 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions',
+ 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.',
+ 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.',
+
+ // actions and triggers
+ 'rule_trigger_user_action' => 'User action is ":trigger_value"',
+ 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"',
+ 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"',
+ 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"',
+ 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"',
+ 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"',
+ 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"',
+ 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"',
+ 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"',
+ 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"',
+ 'rule_trigger_category_is' => 'Category is ":trigger_value"',
+ 'rule_trigger_amount_less' => 'Amount is less than :trigger_value',
+ 'rule_trigger_amount_exactly' => 'Amount is :trigger_value',
+ 'rule_trigger_amount_more' => 'Amount is more than :trigger_value',
+ 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"',
+ 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"',
+ 'rule_trigger_description_contains' => 'Description contains ":trigger_value"',
+ 'rule_trigger_description_is' => 'Description is ":trigger_value"',
+ 'rule_trigger_from_account_starts_choice' => 'Source account starts with..',
+ 'rule_trigger_from_account_ends_choice' => 'Source account ends with..',
+ 'rule_trigger_from_account_is_choice' => 'Source account is..',
+ 'rule_trigger_from_account_contains_choice' => 'Source account contains..',
+ 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..',
+ 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..',
+ 'rule_trigger_to_account_is_choice' => 'Destination account is..',
+ 'rule_trigger_to_account_contains_choice' => 'Destination account contains..',
+ 'rule_trigger_transaction_type_choice' => 'Transaction is of type..',
+ 'rule_trigger_amount_less_choice' => 'Amount is less than..',
+ 'rule_trigger_amount_exactly_choice' => 'Amount is..',
+ 'rule_trigger_amount_more_choice' => 'Amount is more than..',
+ 'rule_trigger_description_starts_choice' => 'Description starts with..',
+ 'rule_trigger_description_ends_choice' => 'Description ends with..',
+ 'rule_trigger_description_contains_choice' => 'Description contains..',
+ 'rule_trigger_description_is_choice' => 'Description is..',
+ 'rule_trigger_category_is_choice' => 'Category is..',
+ 'rule_trigger_budget_is_choice' => 'Budget is..',
+ 'rule_trigger_tag_is_choice' => '(A) tag is..',
+ 'rule_trigger_has_attachments_choice' => 'Has at least this many attachments',
+ 'rule_trigger_has_attachments' => 'Has at least :trigger_value attachment(s)',
+ 'rule_trigger_store_journal' => 'When a transaction is created',
+ 'rule_trigger_update_journal' => 'When a transaction is updated',
+ 'rule_action_set_category' => 'Set category to ":action_value"',
+ 'rule_action_clear_category' => 'Clear category',
+ 'rule_action_set_budget' => 'Set budget to ":action_value"',
+ 'rule_action_clear_budget' => 'Clear budget',
+ 'rule_action_add_tag' => 'Add tag ":action_value"',
+ 'rule_action_remove_tag' => 'Remove tag ":action_value"',
+ 'rule_action_remove_all_tags' => 'Remove all tags',
+ 'rule_action_set_description' => 'Set description to ":action_value"',
+ 'rule_action_append_description' => 'Append description with ":action_value"',
+ 'rule_action_prepend_description' => 'Prepend description with ":action_value"',
+ 'rule_action_set_category_choice' => 'Set category to..',
+ 'rule_action_clear_category_choice' => 'Clear any category',
+ 'rule_action_set_budget_choice' => 'Set budget to..',
+ 'rule_action_clear_budget_choice' => 'Clear any budget',
+ 'rule_action_add_tag_choice' => 'Add tag..',
+ 'rule_action_remove_tag_choice' => 'Remove tag..',
+ 'rule_action_remove_all_tags_choice' => 'Remove all tags',
+ 'rule_action_set_description_choice' => 'Set description to..',
+ 'rule_action_append_description_choice' => 'Append description with..',
+ 'rule_action_prepend_description_choice' => 'Prepend description with..',
+ 'rule_action_set_source_account_choice' => 'Set source account to...',
+ 'rule_action_set_source_account' => 'Set source account to :action_value',
+ 'rule_action_set_destination_account_choice' => 'Set destination account to...',
+ 'rule_action_set_destination_account' => 'Set destination account to :action_value',
+
+ // tags
+ 'store_new_tag' => 'Store new tag',
+ 'update_tag' => 'Update tag',
+ 'no_location_set' => 'No location set.',
+ 'meta_data' => 'Meta data',
+ 'location' => 'Location',
+
+ // preferences
+ 'pref_home_screen_accounts' => 'Счета, отображаемые в сводке',
+ 'pref_home_screen_accounts_help' => 'Какие счета нужно отображать в сводке на главной странице?',
+ 'pref_view_range' => 'View range',
+ 'pref_view_range_help' => 'Некоторые диаграммы автоматически группируются по периодам. Какой период вы предпочитаете?',
+ 'pref_1D' => 'Один день',
+ 'pref_1W' => 'Одна неделя',
+ 'pref_1M' => 'Один месяц',
+ 'pref_3M' => 'Три месяца (квартал)',
+ 'pref_6M' => 'Шесть месяцев',
+ 'pref_1Y' => 'Один год',
+ 'pref_languages' => 'Языки',
+ 'pref_languages_help' => 'Firefly III поддерживает несколько языков. Какой язык вы предпочитаете?',
+ 'pref_custom_fiscal_year' => 'Параметры финансового года',
+ 'pref_custom_fiscal_year_label' => 'Включить',
+ 'pref_custom_fiscal_year_help' => 'Для стран, в которых финансовый год начинается не 1 января, а заканчивается не 31 декабря, вы должны указать даты начала и окончания финансового года',
+ 'pref_fiscal_year_start_label' => 'Дата начала финансового года',
+ 'pref_two_factor_auth' => '2-step verification',
+ 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.',
+ 'pref_enable_two_factor_auth' => 'Enable 2-step verification',
+ 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled',
+ 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!',
+ 'pref_two_factor_auth_code' => 'Verify code',
+ 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.',
+ 'pref_two_factor_auth_reset_code' => 'Reset verification code',
+ 'pref_two_factor_auth_remove_code' => 'Remove verification code',
+ 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)',
+ 'pref_save_settings' => 'Сохранить настройки',
+ 'saved_preferences' => 'Настройки сохранены!',
+ 'preferences_general' => 'Основные',
+ 'preferences_frontpage' => 'Сводка',
+ 'preferences_security' => 'Безопасность',
+ 'preferences_layout' => 'Отображение',
+ 'pref_home_show_deposits' => 'Show deposits on the home screen',
+ 'pref_home_show_deposits_info' => 'The home screen already shows your expense accounts. Should it also show your revenue accounts?',
+ 'pref_home_do_show_deposits' => 'Yes, show them',
+ 'successful_count' => 'of which :count successful',
+ 'transaction_page_size_title' => 'Page size',
+ 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions',
+ 'transaction_page_size_label' => 'Page size',
+ 'between_dates' => '(:start and :end)',
+ 'pref_optional_fields_transaction' => 'Дополнительные поля для транзакций',
+ 'pref_optional_fields_transaction_help' => 'По умолчанию при создании новой транзакции включены не все поля (чтобы не создавать беспорядок). Но вы можете включить эти поля, если лично вам они могут быть полезны. Любое поле, которое в последствии будет отключено, будет по-прежнему отображаться, если оно уже заполнено (независимо от данный настроек).',
+ 'optional_tj_date_fields' => 'Поля с датами',
+ 'optional_tj_business_fields' => 'Бизнес-поля',
+ 'optional_tj_attachment_fields' => 'Поля вложений',
+ 'pref_optional_tj_interest_date' => 'Interest date',
+ 'pref_optional_tj_book_date' => 'Book date',
+ 'pref_optional_tj_process_date' => 'Processing date',
+ 'pref_optional_tj_due_date' => 'Due date',
+ 'pref_optional_tj_payment_date' => 'Payment date',
+ 'pref_optional_tj_invoice_date' => 'Invoice date',
+ 'pref_optional_tj_internal_reference' => 'Internal reference',
+ 'pref_optional_tj_notes' => 'Заметки',
+ 'pref_optional_tj_attachments' => 'Вложения',
+ 'optional_field_meta_dates' => 'Даты',
+ 'optional_field_meta_business' => 'Бизнес',
+ 'optional_field_attachments' => 'Вложения',
+ 'optional_field_meta_data' => 'Optional meta data',
+
+
+ // profile:
+ 'change_your_password' => 'Изменить ваш пароль',
+ 'delete_account' => 'Удалить профиль',
+ 'current_password' => 'Текущий пароль',
+ 'new_password' => 'Новый пароль',
+ 'new_password_again' => 'Новый пароль (ещё раз)',
+ 'delete_your_account' => 'Удалить ваш профиль',
+ 'delete_your_account_help' => 'При удалении вашего профиля также будут удалены все счета, транзакции. Не будет сохранено ничего, что вы хранили в Firefly III. Всё будет УТЕРЯНО!',
+ 'delete_your_account_password' => 'Для продолжения введите свой пароль.',
+ 'password' => 'Пароль',
+ 'are_you_sure' => 'Вы уверены? Эту операцию нельзя будет отменить.',
+ 'delete_account_button' => 'УДАЛИТЬ ваш профиль',
+ 'invalid_current_password' => 'Неправильный пароль!',
+ 'password_changed' => 'Пароль изменён!',
+ 'should_change' => 'Кажется, нужно изменить пароль.',
+ 'invalid_password' => 'Неверный пароль!',
+
+
+ // attachments
+ 'nr_of_attachments' => 'One attachment|:count attachments',
+ 'attachments' => 'Attachments',
+ 'edit_attachment' => 'Edit attachment ":name"',
+ 'update_attachment' => 'Update attachment',
+ 'delete_attachment' => 'Delete attachment ":name"',
+ 'attachment_deleted' => 'Deleted attachment ":name"',
+ 'attachment_updated' => 'Updated attachment ":name"',
+ 'upload_max_file_size' => 'Maximum file size: :size',
+
+ // transaction index
+ 'title_expenses' => 'Expenses',
+ 'title_withdrawal' => 'Expenses',
+ 'title_revenue' => 'Revenue / income',
+ 'title_deposit' => 'Revenue / income',
+ 'title_transfer' => 'Transfers',
+ 'title_transfers' => 'Transfers',
+
+ // convert stuff:
+ 'convert_is_already_type_Withdrawal' => 'This transaction is already a withdrawal',
+ 'convert_is_already_type_Deposit' => 'This transaction is already a deposit',
+ 'convert_is_already_type_Transfer' => 'This transaction is already a transfer',
+ 'convert_to_Withdrawal' => 'Convert ":description" to a withdrawal',
+ 'convert_to_Deposit' => 'Convert ":description" to a deposit',
+ 'convert_to_Transfer' => 'Convert ":description" to a transfer',
+ 'convert_options_WithdrawalDeposit' => 'Convert a withdrawal into a deposit',
+ 'convert_options_WithdrawalTransfer' => 'Convert a withdrawal into a transfer',
+ 'convert_options_DepositTransfer' => 'Convert a deposit into a transfer',
+ 'convert_options_DepositWithdrawal' => 'Convert a deposit into a withdrawal',
+ 'convert_options_TransferWithdrawal' => 'Convert a transfer into a withdrawal',
+ 'convert_options_TransferDeposit' => 'Convert a transfer into a deposit',
+ 'transaction_journal_convert_options' => 'Convert this transaction',
+ 'convert_Withdrawal_to_deposit' => 'Convert this withdrawal to a deposit',
+ 'convert_Withdrawal_to_transfer' => 'Convert this withdrawal to a transfer',
+ 'convert_Deposit_to_withdrawal' => 'Convert this deposit to a withdrawal',
+ 'convert_Deposit_to_transfer' => 'Convert this deposit to a transfer',
+ 'convert_Transfer_to_deposit' => 'Convert this transfer to a deposit',
+ 'convert_Transfer_to_withdrawal' => 'Convert this transfer to a withdrawal',
+ 'convert_please_set_revenue_source' => 'Please pick the revenue account where the money will come from.',
+ 'convert_please_set_asset_destination' => 'Please pick the asset account where the money will go to.',
+ 'convert_please_set_expense_destination' => 'Please pick the expense account where the money will go to.',
+ 'convert_please_set_asset_source' => 'Please pick the asset account where the money will come from.',
+ 'convert_explanation_withdrawal_deposit' => 'If you convert this withdrawal into a deposit, :amount will be deposited into :sourceName instead of taken from it.',
+ 'convert_explanation_withdrawal_transfer' => 'If you convert this withdrawal into a transfer, :amount will be transferred from :sourceName to a new asset account, instead of being paid to :destinationName.',
+ 'convert_explanation_deposit_withdrawal' => 'If you convert this deposit into a withdrawal, :amount will be removed from :destinationName instead of added to it.',
+ 'convert_explanation_deposit_transfer' => 'If you convert this deposit into a transfer, :amount will be transferred from an asset account of your choice into :destinationName.',
+ 'convert_explanation_transfer_withdrawal' => 'If you convert this transfer into a withdrawal, :amount will go from :sourceName to a new destination as an expense, instead of to :destinationName as a transfer.',
+ 'convert_explanation_transfer_deposit' => 'If you convert this transfer into a deposit, :amount will be deposited into account :destinationName instead of being transferred there.',
+ 'converted_to_Withdrawal' => 'The transaction has been converted to a withdrawal',
+ 'converted_to_Deposit' => 'The transaction has been converted to a deposit',
+ 'converted_to_Transfer' => 'The transaction has been converted to a transfer',
+
+
+ // create new stuff:
+ 'create_new_withdrawal' => 'Создать новый расход',
+ 'create_new_deposit' => 'Создать новый доход',
+ 'create_new_transfer' => 'Создать новый перевод',
+ 'create_new_asset' => 'Создать новый активный счёт',
+ 'create_new_expense' => 'Create new expense account',
+ 'create_new_revenue' => 'Create new revenue account',
+ 'create_new_piggy_bank' => 'Создать новую копилку',
+ 'create_new_bill' => 'Create new bill',
+
+ // currencies:
+ 'create_currency' => 'Создать новую валюту',
+ 'store_currency' => 'Сохранить новую валюту',
+ 'update_currency' => 'Обновить валюту',
+ 'new_default_currency' => ':name теперь является вашей основной валютой.',
+ 'cannot_delete_currency' => 'Невозможно удалить валюту :name, поскольку она используется.',
+ 'deleted_currency' => 'Валюта :name удалена',
+ 'created_currency' => 'Валюта :name создана',
+ 'updated_currency' => 'Валюта :name обновлена',
+ 'ask_site_owner' => 'Пожалуйста, обратитесь к :owner для добавления, удаления или изменения валюты.',
+ 'currencies_intro' => 'Firefly III может работать с несколькими валютами. Вы можете управлять ими здесь.',
+ 'make_default_currency' => 'сделать основной',
+ 'default_currency' => 'основная',
+
+ // forms:
+ 'mandatoryFields' => 'Mandatory fields',
+ 'optionalFields' => 'Optional fields',
+ 'options' => 'Параметры',
+
+ // budgets:
+ 'create_new_budget' => 'Create a new budget',
+ 'store_new_budget' => 'Store new budget',
+ 'stored_new_budget' => 'Stored new budget ":name"',
+ 'available_between' => 'Available between :start and :end',
+ 'transactionsWithoutBudget' => 'Expenses without budget',
+ 'transactions_no_budget' => 'Expenses without budget between :start and :end',
+ 'spent_between' => 'Spent between :start and :end',
+ 'createBudget' => 'New budget',
+ 'inactiveBudgets' => 'Inactive budgets',
+ 'without_budget_between' => 'Transactions without a budget between :start and :end',
+ 'budget_in_month' => ':name in :month',
+ 'delete_budget' => 'Delete budget ":name"',
+ 'deleted_budget' => 'Deleted budget ":name"',
+ 'edit_budget' => 'Edit budget ":name"',
+ 'updated_budget' => 'Updated budget ":name"',
+ 'update_amount' => 'Update amount',
+ 'update_budget' => 'Update budget',
+ 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end',
+
+ // bills:
+ 'matching_on' => 'Matching on',
+ 'between_amounts' => 'between :low and :high.',
+ 'repeats' => 'Repeats',
+ 'connected_journals' => 'Connected transactions',
+ 'auto_match_on' => 'Automatically matched by Firefly',
+ 'auto_match_off' => 'Not automatically matched by Firefly',
+ 'next_expected_match' => 'Next expected match',
+ 'delete_bill' => 'Delete bill ":name"',
+ 'deleted_bill' => 'Deleted bill ":name"',
+ 'edit_bill' => 'Edit bill ":name"',
+ 'more' => 'More',
+ 'rescan_old' => 'Rescan old transactions',
+ 'update_bill' => 'Update bill',
+ 'updated_bill' => 'Updated bill ":name"',
+ 'store_new_bill' => 'Store new bill',
+ 'stored_new_bill' => 'Stored new bill ":name"',
+ 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.',
+ 'rescanned_bill' => 'Rescanned everything.',
+ 'average_bill_amount_year' => 'Average bill amount (:year)',
+ 'average_bill_amount_overall' => 'Average bill amount (overall)',
+ 'not_or_not_yet' => 'Not (yet)',
+ 'not_expected_period' => 'Not expected this period',
+ 'bill_is_active' => 'Bill is active',
+ 'bill_will_automatch' => 'Bill will automatically linked to matching transactions',
+ // accounts:
+ 'details_for_asset' => 'Details for asset account ":name"',
+ 'details_for_expense' => 'Details for expense account ":name"',
+ 'details_for_revenue' => 'Details for revenue account ":name"',
+ 'details_for_cash' => 'Details for cash account ":name"',
+ 'store_new_asset_account' => 'Store new asset account',
+ 'store_new_expense_account' => 'Store new expense account',
+ 'store_new_revenue_account' => 'Store new revenue account',
+ 'edit_asset_account' => 'Edit asset account ":name"',
+ 'edit_expense_account' => 'Edit expense account ":name"',
+ 'edit_revenue_account' => 'Edit revenue account ":name"',
+ 'delete_asset_account' => 'Delete asset account ":name"',
+ 'delete_expense_account' => 'Delete expense account ":name"',
+ 'delete_revenue_account' => 'Delete revenue account ":name"',
+ 'asset_deleted' => 'Successfully deleted asset account ":name"',
+ 'expense_deleted' => 'Successfully deleted expense account ":name"',
+ 'revenue_deleted' => 'Successfully deleted revenue account ":name"',
+ 'update_asset_account' => 'Update asset account',
+ 'update_expense_account' => 'Update expense account',
+ 'update_revenue_account' => 'Update revenue account',
+ 'make_new_asset_account' => 'Create a new asset account',
+ 'make_new_expense_account' => 'Create a new expense account',
+ 'make_new_revenue_account' => 'Create a new revenue account',
+ 'asset_accounts' => 'Asset accounts',
+ 'expense_accounts' => 'Expense accounts',
+ 'revenue_accounts' => 'Revenue accounts',
+ 'cash_accounts' => 'Cash accounts',
+ 'Cash account' => 'Cash account',
+ 'account_type' => 'Account type',
+ 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:',
+ 'stored_new_account' => 'New account ":name" stored!',
+ 'updated_account' => 'Updated account ":name"',
+ 'credit_card_options' => 'Credit card options',
+ 'no_transactions_account' => 'There are no transactions (in this period) for asset account ":name".',
+ 'no_data_for_chart' => 'Недостаточно информации (пока) для построения этой диаграммы.',
+ 'select_more_than_one_account' => 'Please select more than one account',
+ 'select_more_than_one_category' => 'Please select more than one category',
+ 'select_more_than_one_budget' => 'Please select more than one budget',
+ 'select_more_than_one_tag' => 'Please select more than one tag',
+ 'from_to' => 'From :start to :end',
+ 'from_to_breadcrumb' => 'from :start to :end',
+ 'account_default_currency' => 'If you select another currency, new transactions from this account will have this currency pre-selected.',
+
+ // categories:
+ 'new_category' => 'Новая категория',
+ 'create_new_category' => 'Создать новую категорию',
+ 'without_category' => 'Без категории',
+ 'update_category' => 'Обновить категорию',
+ 'updated_category' => 'Обновить категорию ":name"',
+ 'categories' => 'Категории',
+ 'edit_category' => 'Изменить категорию ":name"',
+ 'no_category' => '(без категории)',
+ 'category' => 'Категория',
+ 'delete_category' => 'Удалить категорию ":name"',
+ 'deleted_category' => 'Удалить категорию ":name"',
+ 'store_category' => 'Сохранить новую категорию',
+ 'stored_category' => 'Новая категория ":name" успешно сохранена!',
+ 'without_category_between' => 'Without category between :start and :end',
+
+ // transactions:
+ 'update_withdrawal' => 'Update withdrawal',
+ 'update_deposit' => 'Update deposit',
+ 'update_transfer' => 'Update transfer',
+ 'updated_withdrawal' => 'Updated withdrawal ":description"',
+ 'updated_deposit' => 'Updated deposit ":description"',
+ 'updated_transfer' => 'Updated transfer ":description"',
+ 'delete_withdrawal' => 'Delete withdrawal ":description"',
+ 'delete_deposit' => 'Delete deposit ":description"',
+ 'delete_transfer' => 'Delete transfer ":description"',
+ 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"',
+ 'deleted_deposit' => 'Successfully deleted deposit ":description"',
+ 'deleted_transfer' => 'Successfully deleted transfer ":description"',
+ 'stored_journal' => 'Successfully created new transaction ":description"',
+ 'select_transactions' => 'Выбрать транзакции',
+ 'stop_selection' => 'Завершить выбор транзакций',
+ 'edit_selected' => 'Edit selected',
+ 'delete_selected' => 'Delete selected',
+ 'mass_delete_journals' => 'Delete a number of transactions',
+ 'mass_edit_journals' => 'Edit a number of transactions',
+ 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.',
+ 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.',
+ 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).',
+ 'mass_edited_transactions_success' => 'Updated :amount transaction(s)',
+
+
+ // new user:
+ 'welcome' => 'Добро пожаловать в Firefly!',
+ 'submit' => 'Подтвердить',
+ 'getting_started' => 'Начало работы',
+ 'to_get_started' => 'It is good to see you have successfully installed Firefly III. To get started with this tool please enter your bank\'s name and the balance of your main checking account. Do not worry yet if you have multiple accounts. You can add those later. It\'s just that Firefly III needs something to start with.',
+ 'savings_balance_text' => 'Firefly III will automatically create a savings account for you. By default, there will be no money in your savings account, but if you tell Firefly III the balance it will be stored as such.',
+ 'finish_up_new_user' => 'That\'s it! You can continue by pressing Submit. You will be taken to the index of Firefly III.',
+ 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.',
+
+ // home page:
+ 'yourAccounts' => 'Ваши счета',
+ 'budgetsAndSpending' => 'Бюджеты и расходы',
+ 'savings' => 'Savings',
+ 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel',
+ 'createPiggyToContinue' => 'Create piggy banks to fill this panel.',
+ 'newWithdrawal' => 'Новый расход',
+ 'newDeposit' => 'Новый доход',
+ 'newTransfer' => 'Новый перевод',
+ 'moneyIn' => 'Доходы',
+ 'moneyOut' => 'Расходы',
+ 'billsToPay' => 'Счета к оплате',
+ 'billsPaid' => 'Оплаченные счета',
+ 'divided' => 'divided',
+ 'toDivide' => 'left to divide',
+
+ // menu and titles, should be recycled as often as possible:
+ 'currency' => 'Валюта',
+ 'preferences' => 'Настройки',
+ 'logout' => 'Выход',
+ 'searchPlaceholder' => 'Поиск...',
+ 'dashboard' => 'Сводка',
+ 'currencies' => 'Валюты',
+ 'accounts' => 'Счета',
+ 'Asset account' => 'Активный счёт',
+ 'Default account' => 'Основной счёт',
+ 'Expense account' => 'Счета расходов',
+ 'Revenue account' => 'Счета доходов',
+ 'Initial balance account' => 'Начальный баланс для счёта',
+ 'budgets' => 'Бюджет',
+ 'tags' => 'Метки',
+ 'reports' => 'Отчёты',
+ 'transactions' => 'Переводы',
+ 'expenses' => 'Мои расходы',
+ 'income' => 'Мои доходы',
+ 'transfers' => 'Переводы',
+ 'moneyManagement' => 'Управление финансами',
+ 'piggyBanks' => 'Копилки',
+ 'bills' => 'Счета к оплате',
+ 'withdrawal' => 'Расход',
+ 'deposit' => 'Доход',
+ 'account' => 'Счёт',
+ 'transfer' => 'Перевод',
+ 'Withdrawal' => 'Расход',
+ 'Deposit' => 'Доход',
+ 'Transfer' => 'Перевод',
+ 'bill' => 'Счёт к оплате',
+ 'yes' => 'Да',
+ 'no' => 'Нет',
+ 'amount' => 'Сумма',
+ 'overview' => 'Обзор',
+ 'saveOnAccount' => 'Save on account',
+ 'unknown' => 'Unknown',
+ 'daily' => 'Daily',
+ 'monthly' => 'Monthly',
+ 'profile' => 'Профиль',
+ 'errors' => 'Ошибки',
+
+ // reports:
+ 'report_default' => 'Default financial report between :start and :end',
+ 'report_audit' => 'Transaction history overview between :start and :end',
+ 'report_category' => 'Category report between :start and :end',
+ 'report_budget' => 'Budget report between :start and :end',
+ 'report_tag' => 'Tag report between :start and :end',
+ 'quick_link_reports' => 'Quick links',
+ 'quick_link_default_report' => 'Default financial report',
+ 'quick_link_audit_report' => 'Transaction history overview',
+ 'report_this_month_quick' => 'Current month, all accounts',
+ 'report_this_year_quick' => 'Current year, all accounts',
+ 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts',
+ 'report_all_time_quick' => 'All-time, all accounts',
+ 'reports_can_bookmark' => 'Remember that reports can be bookmarked.',
+ 'incomeVsExpenses' => 'Income vs. expenses',
+ 'accountBalances' => 'Account balances',
+ 'balanceStartOfYear' => 'Balance at start of year',
+ 'balanceEndOfYear' => 'Balance at end of year',
+ 'balanceStartOfMonth' => 'Balance at start of month',
+ 'balanceEndOfMonth' => 'Balance at end of month',
+ 'balanceStart' => 'Balance at start of period',
+ 'balanceEnd' => 'Balance at end of period',
+ 'reportsOwnAccounts' => 'Reports for your own accounts',
+ 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts',
+ 'splitByAccount' => 'Split by account',
+ 'balancedByTransfersAndTags' => 'Balanced by transfers and tags',
+ 'coveredWithTags' => 'Covered with tags',
+ 'leftUnbalanced' => 'Left unbalanced',
+ 'expectedBalance' => 'Expected balance',
+ 'outsideOfBudgets' => 'Outside of budgets',
+ 'leftInBudget' => 'Left in budget',
+ 'sumOfSums' => 'Sum of sums',
+ 'noCategory' => '(no category)',
+ 'notCharged' => 'Not charged (yet)',
+ 'inactive' => 'Inactive',
+ 'active' => 'Active',
+ 'difference' => 'Difference',
+ 'in' => 'In',
+ 'out' => 'Out',
+ 'topX' => 'top :number',
+ 'show_full_list' => 'Show entire list',
+ 'show_only_top' => 'Show only top :number',
+ 'sum_of_year' => 'Sum of year',
+ 'sum_of_years' => 'Sum of years',
+ 'average_of_year' => 'Average of year',
+ 'average_of_years' => 'Average of years',
+ 'categories_earned_in_year' => 'Categories (by earnings)',
+ 'categories_spent_in_year' => 'Categories (by spendings)',
+ 'report_type' => 'Report type',
+ 'report_type_default' => 'Default financial report',
+ 'report_type_audit' => 'Transaction history overview (audit)',
+ 'report_type_category' => 'Category report',
+ 'report_type_budget' => 'Budget report',
+ 'report_type_tag' => 'Tag report',
+ 'report_type_meta-history' => 'Categories, budgets and bills overview',
+ 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.',
+ 'report_included_accounts' => 'Included accounts',
+ 'report_date_range' => 'Date range',
+ 'report_preset_ranges' => 'Pre-set ranges',
+ 'shared' => 'Shared',
+ 'fiscal_year' => 'Fiscal year',
+ 'income_entry' => 'Income from account ":name" between :start and :end',
+ 'expense_entry' => 'Expenses to account ":name" between :start and :end',
+ 'category_entry' => 'Expenses in category ":name" between :start and :end',
+ 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end',
+ 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end',
+ 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.',
+ 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance',
+ 'reports_extra_options' => 'Extra options',
+ 'report_has_no_extra_options' => 'This report has no extra options',
+ 'reports_submit' => 'View report',
+ 'end_after_start_date' => 'End date of report must be after start date.',
+ 'select_category' => 'Select category(ies)',
+ 'select_budget' => 'Select budget(s).',
+ 'select_tag' => 'Select tag(s).',
+ 'income_per_category' => 'Income per category',
+ 'expense_per_category' => 'Expense per category',
+ 'expense_per_budget' => 'Expense per budget',
+ 'income_per_account' => 'Income per account',
+ 'expense_per_account' => 'Expense per account',
+ 'expense_per_tag' => 'Expense per tag',
+ 'income_per_tag' => 'Income per tag',
+ 'include_expense_not_in_budget' => 'Included expenses not in the selected budget(s)',
+ 'include_expense_not_in_account' => 'Included expenses not in the selected account(s)',
+ 'include_expense_not_in_category' => 'Included expenses not in the selected category(ies)',
+ 'include_income_not_in_category' => 'Included income not in the selected category(ies)',
+ 'include_income_not_in_account' => 'Included income not in the selected account(s)',
+ 'include_income_not_in_tags' => 'Included income not in the selected tag(s)',
+ 'include_expense_not_in_tags' => 'Included expenses not in the selected tag(s)',
+ 'everything_else' => 'Everything else',
+ 'income_and_expenses' => 'Income and expenses',
+ 'spent_average' => 'Spent (average)',
+ 'income_average' => 'Income (average)',
+ 'transaction_count' => 'Transaction count',
+ 'average_spending_per_account' => 'Average spending per account',
+ 'average_income_per_account' => 'Average income per account',
+ 'total' => 'Итого',
+ 'description' => 'Описание',
+ 'sum_of_period' => 'Sum of period',
+ 'average_in_period' => 'Average in period',
+ 'account_role_defaultAsset' => 'Счёт по умолчанию',
+ 'account_role_sharedAsset' => 'Shared asset account',
+ 'account_role_savingAsset' => 'Savings account',
+ 'account_role_ccAsset' => 'Credit card',
+
+ // charts:
+ 'chart' => 'Диаграмма',
+ 'dayOfMonth' => 'Day of the month',
+ 'month' => 'Month',
+ 'budget' => 'Budget',
+ 'spent' => 'Spent',
+ 'spent_in_budget' => 'Spent in budget',
+ 'left_to_spend' => 'Left to spend',
+ 'earned' => 'Заработано',
+ 'overspent' => 'Overspent',
+ 'left' => 'Left',
+ 'no_budget' => '(no budget)',
+ 'max-amount' => 'Maximum amount',
+ 'min-amount' => 'Minumum amount',
+ 'journal-amount' => 'Current bill entry',
+ 'name' => 'Name',
+ 'date' => 'Date',
+ 'paid' => 'Paid',
+ 'unpaid' => 'Unpaid',
+ 'day' => 'Day',
+ 'budgeted' => 'Budgeted',
+ 'period' => 'Period',
+ 'balance' => 'Balance',
+ 'summary' => 'Summary',
+ 'sum' => 'Sum',
+ 'average' => 'Average',
+ 'balanceFor' => 'Balance for :name',
+
+ // piggy banks:
+ 'add_money_to_piggy' => 'Add money to piggy bank ":name"',
+ 'piggy_bank' => 'Piggy bank',
+ 'new_piggy_bank' => 'New piggy bank',
+ 'store_piggy_bank' => 'Store new piggy bank',
+ 'stored_piggy_bank' => 'Store new piggy bank ":name"',
+ 'account_status' => 'Account status',
+ 'left_for_piggy_banks' => 'Left for piggy banks',
+ 'sum_of_piggy_banks' => 'Sum of piggy banks',
+ 'saved_so_far' => 'Saved so far',
+ 'left_to_save' => 'Left to save',
+ 'suggested_amount' => 'Suggested monthly amount to save',
+ 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"',
+ 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"',
+ 'add' => 'Add',
+
+ 'remove' => 'Remove',
+ 'max_amount_add' => 'The maximum amount you can add is',
+ 'max_amount_remove' => 'The maximum amount you can remove is',
+ 'update_piggy_button' => 'Update piggy bank',
+ 'update_piggy_title' => 'Update piggy bank ":name"',
+ 'updated_piggy_bank' => 'Updated piggy bank ":name"',
+ 'details' => 'Details',
+ 'events' => 'Events',
+ 'target_amount' => 'Target amount',
+ 'start_date' => 'Start date',
+ 'target_date' => 'Target date',
+ 'no_target_date' => 'No target date',
+ 'todo' => 'to do',
+ 'table' => 'Table',
+ 'piggy_bank_not_exists' => 'Piggy bank no longer exists.',
+ 'add_any_amount_to_piggy' => 'Add money to this piggy bank to reach your target of :amount.',
+ 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date',
+ 'delete_piggy_bank' => 'Delete piggy bank ":name"',
+ 'cannot_add_amount_piggy' => 'Could not add :amount to ":name".',
+ 'cannot_remove_from_piggy' => 'Could not remove :amount from ":name".',
+ 'deleted_piggy_bank' => 'Deleted piggy bank ":name"',
+ 'added_amount_to_piggy' => 'Added :amount to ":name"',
+ 'removed_amount_from_piggy' => 'Removed :amount from ":name"',
+ 'cannot_remove_amount_piggy' => 'Could not remove :amount from ":name".',
+
+ // tags
+ 'regular_tag' => 'Just a regular tag.',
+ 'balancing_act' => 'The tag takes at most two transactions; an expense and a transfer. They\'ll balance each other out.',
+ 'advance_payment' => 'The tag accepts one expense and any number of deposits aimed to repay the original expense.',
+ 'delete_tag' => 'Delete tag ":tag"',
+ 'deleted_tag' => 'Deleted tag ":tag"',
+ 'new_tag' => 'Make new tag',
+ 'edit_tag' => 'Edit tag ":tag"',
+ 'updated_tag' => 'Updated tag ":tag"',
+ 'created_tag' => 'Tag ":tag" has been created!',
+ 'no_year' => 'No year set',
+ 'no_month' => 'No month set',
+ 'tag_title_nothing' => 'Default tags',
+ 'tag_title_balancingAct' => 'Balancing act tags',
+ 'tag_title_advancePayment' => 'Advance payment tags',
+ 'tags_introduction' => 'Usually tags are singular words, designed to quickly band items together using things like expensive, bill or for-party. In Firefly III, tags can have more properties such as a date, description and location. This allows you to join transactions together in a more meaningful way. For example, you could make a tag called Christmas dinner with friends and add information about the restaurant. Such tags are "singular", you would only use them for a single occasion, perhaps with multiple transactions.',
+ 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.',
+ 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.',
+
+ 'transaction_journal_information' => 'Transaction information',
+ 'transaction_journal_meta' => 'Meta information',
+ 'total_amount' => 'Total amount',
+ 'number_of_decimals' => 'Number of decimals',
+
+ // administration
+ 'administration' => 'Администрирование',
+ 'user_administration' => 'Управление пользователями',
+ 'list_all_users' => 'Список пользователей\'',
+ 'all_users' => 'Все пользователи',
+ 'instance_configuration' => 'Конфигурация',
+ 'firefly_instance_configuration' => 'Базовая конфигурация Firefly III',
+ 'setting_single_user_mode' => 'Single user mode',
+ 'setting_single_user_mode_explain' => 'By default, Firefly III only accepts one (1) registration: you. This is a security measure, preventing others from using your instance unless you allow them to. Future registrations are blocked. When you uncheck this box, others can use your instance as wel, assuming they can reach it (when it is connected to the internet).',
+ 'store_configuration' => 'Store configuration',
+ 'single_user_administration' => 'User administration for :email',
+ 'edit_user' => 'Edit user :email',
+ 'hidden_fields_preferences' => 'Not all fields are visible right now. You must enable them in your settings.',
+ 'user_data_information' => 'User data',
+ 'user_information' => 'User information',
+ 'total_size' => 'общий размер',
+ 'budget_or_budgets' => 'budget(s)',
+ 'budgets_with_limits' => 'budget(s) with configured amount',
+ 'rule_or_rules' => 'rule(s)',
+ 'rulegroup_or_groups' => 'rule group(s)',
+ 'setting_must_confirm_account' => 'Account confirmation',
+ 'setting_must_confirm_account_explain' => 'When this setting is enabled, users must activate their account before it can be used.',
+ 'configuration_updated' => 'The configuration has been updated',
+ 'setting_is_demo_site' => 'Demo site',
+ 'setting_is_demo_site_explain' => 'If you check this box, this installation will behave as if it is the demo site, which can have weird side effects.',
+ 'setting_send_email_notifications' => 'Send email notifications',
+ 'setting_send_email_explain' => 'Firefly III can send you email notifications about certain events. They will be sent to :site_owner
. This email address can be set in the .env
file.',
+ 'block_code_bounced' => 'Email message(s) bounced',
+ 'block_code_expired' => 'Demo account expired',
+ 'no_block_code' => 'No reason for block or user not blocked',
+
+
+ // split a transaction:
+ 'transaction_meta_data' => 'Transaction meta-data',
+ 'transaction_dates' => 'Transaction dates',
+ 'splits' => 'Splits',
+ 'split_title_withdrawal' => 'Split your new withdrawal',
+ 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.',
+ 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.',
+ 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.',
+ 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
+ 'store_splitted_withdrawal' => 'Store splitted withdrawal',
+ 'update_splitted_withdrawal' => 'Update splitted withdrawal',
+ 'split_title_deposit' => 'Split your new deposit',
+ 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.',
+ 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.',
+ 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.',
+ 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
+ 'store_splitted_deposit' => 'Store splitted deposit',
+ 'split_title_transfer' => 'Split your new transfer',
+ 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.',
+ 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.',
+ 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.',
+ 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
+ 'store_splitted_transfer' => 'Store splitted transfer',
+ 'add_another_split' => 'Add another split',
+ 'split-transactions' => 'Split transactions',
+ 'split-new-transaction' => 'Split a new transaction',
+ 'do_split' => 'Do a split',
+ 'split_this_withdrawal' => 'Split this withdrawal',
+ 'split_this_deposit' => 'Split this deposit',
+ 'split_this_transfer' => 'Split this transfer',
+ 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.',
+ 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.',
+ 'cannot_edit_opening_balance' => 'You cannot edit the opening balance of an account.',
+ 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.',
+
+ // import bread crumbs and titles:
+ 'import' => 'Импорт',
+ 'import_data' => 'Импорт данных',
+
+ // import index page:
+ 'import_index_title' => 'Импорт данных в Firefly III',
+ 'import_index_sub_title' => 'Index',
+ 'import_index_intro' => 'Welcome to Firefly\'s import routine. These pages can help you import data from your bank into Firefly III. Please check out the help pages in the top right corner.',
+ 'import_index_file' => 'Select your file',
+ 'import_index_config' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.',
+ 'import_index_type' => 'Select the type of file you will upload',
+ 'import_index_start' => 'Start importing',
+
+ // supported file types:
+ 'import_file_type_csv' => 'CSV (comma separated values)',
+
+ // import configuration routine:
+ 'import_config_sub_title' => 'Set up your import file',
+ 'import_config_bread_crumb' => 'Set up your import file',
+
+ // import status page:
+ 'import_status_bread_crumb' => 'Import status',
+ 'import_status_sub_title' => 'Import status',
+ 'import_status_wait_title' => 'Please hold...',
+ 'import_status_wait_text' => 'This box will disappear in a moment.',
+ 'import_status_ready_title' => 'Import is ready to start',
+ 'import_status_ready_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.',
+ 'import_status_ready_config' => 'Download configuration',
+ 'import_status_ready_start' => 'Start the import',
+ 'import_status_ready_share' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.',
+ 'import_status_running_title' => 'The import is running',
+ 'import_status_running_placeholder' => 'Please hold for an update...',
+ 'import_status_errors_title' => 'Errors during the import',
+ 'import_status_errors_single' => 'An error has occured during the import. It does not appear to be fatal.',
+ 'import_status_errors_multi' => 'Some errors occured during the import. These do not appear to be fatal.',
+ 'import_status_fatal_title' => 'A fatal error occurred',
+ 'import_status_fatal_text' => 'A fatal error occurred, which the import-routine cannot recover from. Please see the explanation in red below.',
+ 'import_status_fatal_more' => 'If the error is a time-out, the import will have stopped half-way. For some server configurations, it is merely the server that stopped while the import keeps running in the background. To verify this, check out the log files. If the problem persists, consider importing over the command line instead.',
+ 'import_status_finished_title' => 'Import routine finished',
+ 'import_status_finished_text' => 'The import routine has imported your file.',
+ 'import_status_finished_job' => 'The transactions imported can be found in tag :tag.',
+ 'import_with_key' => 'Import with key \':key\'',
+
+ // different states:
+ 'import_status_job_running' => 'The import is underway. Please be patient...',
+
+ // sandstorm.io errors and messages:
+ 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.',
+
+ // empty lists? no objects? instructions:
+ 'no_transactions_in_period' => 'There are no transactions in this period.',
+ 'no_accounts_title_asset' => 'Let\'s create an asset account!',
+ 'no_accounts_intro_asset' => 'You have no asset accounts yet. Asset accounts are your main accounts: your checking account, savings account, shared account or even your credit card.',
+ 'no_accounts_imperative_asset' => 'To start using Firefly III you must create at least one asset account. Let\'s do so now:',
+ 'no_accounts_create_asset' => 'Создание расходного счёта',
+ 'no_accounts_title_expense' => 'Давайте создадим расходный счёт!',
+ 'no_accounts_intro_expense' => 'Пока у вас нет ни одного расходного счёта. Расходные счета используются для контроля за тем, где именно вы расходуете свои финансы. Например, в магазинах или на оплату коммунальных счетов.',
+ 'no_accounts_imperative_expense' => 'Расходные счета создаются автоматически, когда вы создаёте транзакции (расходы), но вы можете также создать их вручную, если хотите. Давайте создадим один прямо сейчас:',
+ 'no_accounts_create_expense' => 'Создать расходный счёт',
+ 'no_accounts_title_revenue' => 'Let\'s create a revenue account!',
+ 'no_accounts_intro_revenue' => 'You have no revenue accounts yet. Revenue accounts are the places where you receive money from, such as your employer.',
+ 'no_accounts_imperative_revenue' => 'Revenue accounts are created automatically when you create transactions, but you can create one manually too, if you want. Let\'s create one now:',
+ 'no_accounts_create_revenue' => 'Create a revenue account',
+ 'no_budgets_title_default' => 'Let\'s create a budget',
+ 'no_budgets_intro_default' => 'You have no budgets yet. Budgets are used to organise your expenses into logical groups, which you can give a soft-cap to limit your expenses.',
+ 'no_budgets_imperative_default' => 'Budgets are the basic tools of financial management. Let\'s create one now:',
+ 'no_budgets_create_default' => 'Create a budget',
+ 'no_categories_title_default' => 'Let\'s create a category!',
+ 'no_categories_intro_default' => 'You have no categories yet. Categories are used to fine tune your transactions and label them with their designated category.',
+ 'no_categories_imperative_default' => 'Categories are created automatically when you create transactions, but you can create one manually too. Let\'s create one now:',
+ 'no_categories_create_default' => 'Create a category',
+ 'no_tags_title_default' => 'Let\'s create a tag!',
+ 'no_tags_intro_default' => 'You have no tags yet. Tags are used to fine tune your transactions and label them with specific keywords.',
+ 'no_tags_imperative_default' => 'Tags are created automatically when you create transactions, but you can create one manually too. Let\'s create one now:',
+ 'no_tags_create_default' => 'Create a tag',
+ 'no_transactions_title_withdrawal' => 'Let\'s create an expense!',
+ 'no_transactions_intro_withdrawal' => 'You have no expenses yet. You should create expenses to start managing your finances.',
+ 'no_transactions_imperative_withdrawal' => 'Have you spent some money? Then you should write it down:',
+ 'no_transactions_create_withdrawal' => 'Create an expense',
+ 'no_transactions_title_deposit' => 'Let\'s create some income!',
+ 'no_transactions_intro_deposit' => 'You have no recorded income yet. You should create income entries to start managing your finances.',
+ 'no_transactions_imperative_deposit' => 'Have you received some money? Then you should write it down:',
+ 'no_transactions_create_deposit' => 'Create a deposit',
+ 'no_transactions_title_transfers' => 'Let\'s create a transfer!',
+ 'no_transactions_intro_transfers' => 'You have no transfers yet. When you move money between asset accounts, it is recorded as a transfer.',
+ 'no_transactions_imperative_transfers' => 'Have you moved some money around? Then you should write it down:',
+ 'no_transactions_create_transfers' => 'Create a transfer',
+ 'no_piggies_title_default' => 'Let\'s create a piggy bank!',
+ 'no_piggies_intro_default' => 'You have no piggy banks yet. You can create piggy banks to divide your savings and keep track of what you\'re saving up for.',
+ 'no_piggies_imperative_default' => 'Do you have things you\'re saving money for? Create a piggy bank and keep track:',
+ 'no_piggies_create_default' => 'Create a new piggy bank',
+ 'no_bills_title_default' => 'Let\'s create a bill!',
+ 'no_bills_intro_default' => 'You have no bills yet. You can create bills to keep track of regular expenses, like your rent of insurance.',
+ 'no_bills_imperative_default' => 'Do you have such regular bills? Create a bill and keep track of your payments:',
+ 'no_bills_create_default' => 'Create a bill',
+
+
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/form.php b/resources/lang/ru_RU/form.php
new file mode 100644
index 0000000000..c25d3120aa
--- /dev/null
+++ b/resources/lang/ru_RU/form.php
@@ -0,0 +1,189 @@
+ 'Bank name',
+ 'bank_balance' => 'Balance',
+ 'savings_balance' => 'Savings balance',
+ 'credit_card_limit' => 'Credit card limit',
+ 'automatch' => 'Match automatically',
+ 'skip' => 'Skip',
+ 'name' => 'Name',
+ 'active' => 'Active',
+ 'amount_min' => 'Minimum amount',
+ 'amount_max' => 'Maximum amount',
+ 'match' => 'Matches on',
+ 'repeat_freq' => 'Repeats',
+ 'journal_currency_id' => 'Currency',
+ 'currency_id' => 'Currency',
+ 'attachments' => 'Attachments',
+ 'journal_amount' => 'Amount',
+ 'journal_asset_source_account' => 'Asset account (source)',
+ 'journal_source_account_name' => 'Revenue account (source)',
+ 'journal_source_account_id' => 'Asset account (source)',
+ 'BIC' => 'BIC',
+ 'account_from_id' => 'From account',
+ 'account_to_id' => 'To account',
+ 'source_account' => 'Source account',
+ 'destination_account' => 'Destination account',
+ 'journal_destination_account_id' => 'Asset account (destination)',
+ 'asset_destination_account' => 'Asset account (destination)',
+ 'asset_source_account' => 'Asset account (source)',
+ 'journal_description' => 'Description',
+ 'note' => 'Notes',
+ 'split_journal' => 'Split this transaction',
+ 'split_journal_explanation' => 'Split this transaction in multiple parts',
+ 'currency' => 'Currency',
+ 'account_id' => 'Asset account',
+ 'budget_id' => 'Budget',
+ 'openingBalance' => 'Opening balance',
+ 'tagMode' => 'Tag mode',
+ 'tagPosition' => 'Tag location',
+ 'virtualBalance' => 'Virtual balance',
+ 'longitude_latitude' => 'Location',
+ 'targetamount' => 'Target amount',
+ 'accountRole' => 'Account role',
+ 'openingBalanceDate' => 'Opening balance date',
+ 'ccType' => 'Credit card payment plan',
+ 'ccMonthlyPaymentDate' => 'Credit card monthly payment date',
+ 'piggy_bank_id' => 'Piggy bank',
+ 'returnHere' => 'Return here',
+ 'returnHereExplanation' => 'After storing, return here to create another one.',
+ 'returnHereUpdateExplanation' => 'After updating, return here.',
+ 'description' => 'Description',
+ 'expense_account' => 'Expense account',
+ 'revenue_account' => 'Revenue account',
+ 'decimal_places' => 'Decimal places',
+ 'exchange_rate_instruction' => 'Foreign currencies',
+ 'exchanged_amount' => 'Exchanged amount',
+ 'source_amount' => 'Amount (source)',
+ 'destination_amount' => 'Amount (destination)',
+ 'native_amount' => 'Native amount',
+
+ 'revenue_account_source' => 'Revenue account (source)',
+ 'source_account_asset' => 'Source account (asset account)',
+ 'destination_account_expense' => 'Destination account (expense account)',
+ 'destination_account_asset' => 'Destination account (asset account)',
+ 'source_account_revenue' => 'Source account (revenue account)',
+ 'type' => 'Type',
+ 'convert_Withdrawal' => 'Convert withdrawal',
+ 'convert_Deposit' => 'Convert deposit',
+ 'convert_Transfer' => 'Convert transfer',
+
+
+ 'amount' => 'Amount',
+ 'date' => 'Date',
+ 'interest_date' => 'Interest date',
+ 'book_date' => 'Book date',
+ 'process_date' => 'Processing date',
+ 'category' => 'Category',
+ 'tags' => 'Tags',
+ 'deletePermanently' => 'Delete permanently',
+ 'cancel' => 'Cancel',
+ 'targetdate' => 'Target date',
+ 'tag' => 'Tag',
+ 'under' => 'Under',
+ 'symbol' => 'Symbol',
+ 'code' => 'Code',
+ 'iban' => 'IBAN',
+ 'accountNumber' => 'Account number',
+ 'has_headers' => 'Headers',
+ 'date_format' => 'Date format',
+ 'specifix' => 'Bank- or file specific fixes',
+ 'attachments[]' => 'Attachments',
+ 'store_new_withdrawal' => 'Store new withdrawal',
+ 'store_new_deposit' => 'Store new deposit',
+ 'store_new_transfer' => 'Store new transfer',
+ 'add_new_withdrawal' => 'Add a new withdrawal',
+ 'add_new_deposit' => 'Add a new deposit',
+ 'add_new_transfer' => 'Add a new transfer',
+ 'noPiggybank' => '(no piggy bank)',
+ 'title' => 'Title',
+ 'notes' => 'Notes',
+ 'filename' => 'File name',
+ 'mime' => 'Mime type',
+ 'size' => 'Size',
+ 'trigger' => 'Trigger',
+ 'stop_processing' => 'Stop processing',
+ 'start_date' => 'Start of range',
+ 'end_date' => 'End of range',
+ 'export_start_range' => 'Start of export range',
+ 'export_end_range' => 'End of export range',
+ 'export_format' => 'File format',
+ 'include_attachments' => 'Include uploaded attachments',
+ 'include_old_uploads' => 'Include imported data',
+ 'accounts' => 'Export transactions from these accounts',
+ 'delete_account' => 'Delete account ":name"',
+ 'delete_bill' => 'Delete bill ":name"',
+ 'delete_budget' => 'Delete budget ":name"',
+ 'delete_category' => 'Delete category ":name"',
+ 'delete_currency' => 'Delete currency ":name"',
+ 'delete_journal' => 'Delete transaction with description ":description"',
+ 'delete_attachment' => 'Delete attachment ":name"',
+ 'delete_rule' => 'Delete rule ":title"',
+ 'delete_rule_group' => 'Delete rule group ":title"',
+ 'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
+ 'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
+ 'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
+ 'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?',
+ 'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?',
+ 'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
+ 'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?',
+ 'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
+ 'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
+ 'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
+ 'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?',
+ 'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
+ 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
+ 'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.',
+ 'delete_all_permanently' => 'Delete selected permanently',
+ 'update_all_journals' => 'Update these transactions',
+ 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
+ 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
+ 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
+ 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
+ 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
+ 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
+ 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
+
+ 'email' => 'Email address',
+ 'password' => 'Password',
+ 'password_confirmation' => 'Password (again)',
+ 'blocked' => 'Is blocked?',
+ 'blocked_code' => 'Reason for block',
+
+
+ // admin
+ 'domain' => 'Domain',
+ 'single_user_mode' => 'Single user mode',
+ 'must_confirm_account' => 'New users must activate account',
+ 'is_demo_site' => 'Is demo site',
+
+
+ // import
+ 'import_file' => 'Файл импорта',
+ 'configuration_file' => 'Файл конфигурации',
+ 'import_file_type' => 'Тип файла для импорта',
+ 'csv_comma' => 'Запятая (,)',
+ 'csv_semicolon' => 'Точка с запятой (;)',
+ 'csv_tab' => 'Табулятор (невидимый)',
+ 'csv_delimiter' => 'Разделитель полей CSV',
+ 'csv_import_account' => 'Профиль для импорта по умолчанию',
+ 'csv_config' => 'Параметры импорта CSV',
+
+
+ 'due_date' => 'Due date',
+ 'payment_date' => 'Payment date',
+ 'invoice_date' => 'Invoice date',
+ 'internal_reference' => 'Internal reference',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/help.php b/resources/lang/ru_RU/help.php
new file mode 100644
index 0000000000..021cfc1459
--- /dev/null
+++ b/resources/lang/ru_RU/help.php
@@ -0,0 +1,33 @@
+ 'Добро пожаловать в Firefly III',
+ 'main-content-text' => 'Пожалуйста, ознакомьтесь с этим кратким руководством, чтобы убедиться, что вы знаете, куда идти дальше.',
+ 'sidebar-toggle-title' => 'Основные операции доступны в боковой панели',
+ 'sidebar-toggle-text' => 'Под значком со знаком "+" скрываются кнопки для создания новых счётов, расходов, переводов и других операций!',
+ 'account-menu-title' => 'Все ваши счета',
+ 'account-menu-text' => 'Здесь вы найдёте все созданные вами счета.',
+ 'budget-menu-title' => 'Бюджет',
+ 'budget-menu-text' => 'Используйте эту страницу, чтобы упорядочить свои финансы и ограничить расходы.',
+ 'report-menu-title' => 'Отчёты',
+ 'report-menu-text' => 'Взгляните сюда, чтобы увидеть свои финансы с разных сторон.',
+ 'transaction-menu-title' => 'Транзакции',
+ 'transaction-menu-text' => 'Все ваши финансовые операции (транзакции) можно увидеть здесь.',
+ 'option-menu-title' => 'Настройки',
+ 'option-menu-text' => 'Все остальное не требует объяснений.',
+ 'main-content-end-title' => 'Конец!',
+ 'main-content-end-text' => 'Помните, что в правом верхнем углу любой страницы есть небольшой знак вопроса. Нажмите на него, чтобы получить справку о той странице, на которой вы находитесь.',
+ 'index' => 'оглавление',
+ 'home' => 'главная',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/intro.php b/resources/lang/ru_RU/intro.php
new file mode 100644
index 0000000000..7fb42114a8
--- /dev/null
+++ b/resources/lang/ru_RU/intro.php
@@ -0,0 +1,118 @@
+ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.',
+ 'index_accounts-chart' => 'This chart shows the current balance of your asset accounts. You can select the accounts visible here in your preferences.',
+ 'index_box_out_holder' => 'This little box and the boxes next to this one will give you a quick overview of your financial situation.',
+ 'index_help' => 'If you ever need help with a page or a form, press this button.',
+ 'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
+ 'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
+
+ // create account:
+ 'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
+ 'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly.',
+ 'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
+ 'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
+
+ // budgets index
+ 'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
+ 'budgets_index_set_budget' => 'Set your total budget for every period so Firefly can tell you if you have budgeted all available money.',
+ 'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
+ 'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
+ 'budgets_index_new_budget' => 'Create new budgets as you see fit.',
+ 'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
+
+ // reports (index)
+ 'reports_index_intro' => 'Use these reports to get detailed insights in your finances.',
+ 'reports_index_inputReportType' => 'Pick a report type. Check out the help pages to see what each report shows you.',
+ 'reports_index_inputAccountsSelect' => 'You can exclude or include asset accounts as you see fit.',
+ 'reports_index_inputDateRange' => 'The selected date range is entirely up to you: from one day to 10 years.',
+ 'reports_index_extra-options-box' => 'Depending on the report you have selected, you can select extra filters and options here. Watch this box when you change report types.',
+
+ // reports (reports)
+ 'reports_report_default_intro' => 'This report will give you a quick and comprehensive overview of your finances. If you wish to see anything else, please don\'t hestitate to contact me!',
+ 'reports_report_audit_intro' => 'This report will give you detailed insights in your asset accounts.',
+ 'reports_report_audit_optionsBox' => 'Use these check boxes to show or hide the columns you are interested in.',
+
+ 'reports_report_category_intro' => 'This report will give you insight in or multiple categories.',
+ 'reports_report_category_pieCharts' => 'These charts will give you insight in expenses and income per category or per account.',
+ 'reports_report_category_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per category.',
+
+ 'reports_report_tag_intro' => 'This report will give you insight in or multiple tags.',
+ 'reports_report_tag_pieCharts' => 'These charts will give you insight in expenses and income per tag, account, category or budget.',
+ 'reports_report_tag_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per tag.',
+
+ 'reports_report_budget_intro' => 'This report will give you insight in or multiple budgets.',
+ 'reports_report_budget_pieCharts' => 'These charts will give you insight in expenses and income per budget or per account.',
+ 'reports_report_budget_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per budget.',
+
+ // create transaction
+ 'transactions_create_switch_box' => 'Use these buttons to quickly switch the type of transaction you wish to save.',
+ 'transactions_create_ffInput_category' => 'You can freely type in this field. Previously created categories will be suggested.',
+ 'transactions_create_withdrawal_ffInput_budget' => 'Link your withdrawal to a budget for better financial control.',
+ 'transactions_create_withdrawal_currency_dropdown_amount' => 'Use this dropdown when your withdrawal is in another currency.',
+ 'transactions_create_deposit_currency_dropdown_amount' => 'Use this dropdown when your deposit is in another currency.',
+ 'transactions_create_transfer_ffInput_piggy_bank_id' => 'Select a piggy bank and link this transfer to your savings.',
+
+ // piggy banks index:
+ 'piggy-banks_index_saved' => 'This field shows you how much you\'ve saved in each piggy bank.',
+ 'piggy-banks_index_button' => 'Next to this progress bar are two buttons (+ and -) to add or remove money from each piggy bank.',
+ 'piggy-banks_index_accountStatus' => 'For each asset account with at least one piggy bank the status is listed in this table.',
+
+ // create piggy
+ 'piggy-banks_create_name' => 'What is your goal? A new couch, a camera, money for emergencies?',
+ 'piggy-banks_create_date' => 'You can set a target date or a deadline for your piggy bank.',
+
+ // show piggy
+ 'piggy-banks_show_piggyChart' => 'This chart will show the history of this piggy bank.',
+ 'piggy-banks_show_piggyDetails' => 'Some details about your piggy bank',
+ 'piggy-banks_show_piggyEvents' => 'Any additions or removals are also listed here.',
+
+ // bill index
+ 'bills_index_paid_in_period' => 'This field indicates when the bill was last paid.',
+ 'bills_index_expected_in_period' => 'This field indicates for each bill if and when the next bill is expected to hit.',
+
+ // show bill
+ 'bills_show_billInfo' => 'This table shows some general information about this bill.',
+ 'bills_show_billButtons' => 'Use this button to re-scan old transactions so they will be matched to this bill.',
+ 'bills_show_billChart' => 'This chart shows the transactions linked to this bill.',
+
+ // create bill
+ 'bills_create_name' => 'Use a descriptive name such as "Rent" or "Health insurance".',
+ 'bills_create_match' => 'To match transactions, use terms from those transactions or the expense account involved. All words must match.',
+ 'bills_create_amount_min_holder' => 'Select a minimum and maximum amount for this bill.',
+ 'bills_create_repeat_freq_holder' => 'Most bills repeat monthly, but you can set another frequency here.',
+ 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks for example, the "skip"-field should be set to "1" to skip every other week.',
+
+ // rules index
+ 'rules_index_intro' => 'Firefly III allows you to manage rules, that will automagically be applied to any transaction you create or edit.',
+ 'rules_index_new_rule_group' => 'You can combine rules in groups for easier management.',
+ 'rules_index_new_rule' => 'Create as many rules as you like.',
+ 'rules_index_prio_buttons' => 'Order them any way you see fit.',
+ 'rules_index_test_buttons' => 'You can test your rules or apply them to existing transactions.',
+ 'rules_index_rule-triggers' => 'Rules have "triggers" and "actions" that you can order by drag-and-drop.',
+ 'rules_index_outro' => 'Be sure to check out the help pages using the (?) icon in the top right!',
+
+ // create rule:
+ 'rules_create_mandatory' => 'Choose a descriptive title, and set when the rule should be fired.',
+ 'rules_create_ruletriggerholder' => 'Add as many triggers as you like, but remember that ALL triggers must match before any actions are fired.',
+ 'rules_create_test_rule_triggers' => 'Use this button to see which transactions would match your rule.',
+ 'rules_create_actions' => 'Set as many actions as you like.',
+
+ // preferences
+ 'preferences_index_tabs' => 'More options are available behind these tabs.',
+
+ // currencies
+ 'currencies_index_default' => 'Firefly III has one default currency. You can always switch of course using these buttons.',
+
+ // create currency
+ 'currencies_create_code' => 'This code should be ISO compliant (Google it for your new currency).',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/list.php b/resources/lang/ru_RU/list.php
new file mode 100644
index 0000000000..6e39c0b336
--- /dev/null
+++ b/resources/lang/ru_RU/list.php
@@ -0,0 +1,89 @@
+ 'Кнопки',
+ 'icon' => 'Значок',
+ 'id' => 'ID',
+ 'create_date' => 'Создан',
+ 'update_date' => 'Обновлён',
+ 'balance_before' => 'Баланс до',
+ 'balance_after' => 'Баланс после',
+ 'name' => 'Имя',
+ 'role' => 'Роль',
+ 'currentBalance' => 'Текущий баланс',
+ 'active' => 'Активен?',
+ 'lastActivity' => 'Последняя активность',
+ 'balanceDiff' => 'Разница баланса между :start и :end',
+ 'matchedOn' => 'Matched on',
+ 'matchesOn' => 'Matched on',
+ 'account_type' => 'Тип профиля',
+ 'created_at' => 'Created at',
+ 'new_balance' => 'New balance',
+ 'account' => 'Account',
+ 'matchingAmount' => 'Amount',
+ 'lastMatch' => 'Last match',
+ 'split_number' => 'Split #',
+ 'destination' => 'Destination',
+ 'source' => 'Source',
+ 'next_expected_match' => 'Next expected match',
+ 'automatch' => 'Auto match?',
+ 'repeat_freq' => 'Repeats',
+ 'description' => 'Описание',
+ 'amount' => 'Сумма',
+ 'internal_reference' => 'Internal reference',
+ 'date' => 'Дата',
+ 'interest_date' => 'Interest date',
+ 'book_date' => 'Book date',
+ 'process_date' => 'Processing date',
+ 'due_date' => 'Due date',
+ 'payment_date' => 'Payment date',
+ 'invoice_date' => 'Invoice date',
+ 'interal_reference' => 'Internal reference',
+ 'notes' => 'Заметки',
+ 'from' => 'Откуда',
+ 'piggy_bank' => 'Копилка',
+ 'to' => 'Куда',
+ 'budget' => 'Бюджет',
+ 'category' => 'Категория',
+ 'bill' => 'Счет к оплате',
+ 'withdrawal' => 'Расход',
+ 'deposit' => 'Доход',
+ 'transfer' => 'Перевод',
+ 'type' => 'Type',
+ 'completed' => 'Completed',
+ 'iban' => 'IBAN',
+ 'paid_current_period' => 'Paid this period',
+ 'email' => 'E-mail',
+ 'registered_at' => 'Дата регистрации',
+ 'is_activated' => 'Активен?',
+ 'is_blocked' => 'Заблокирован?',
+ 'is_admin' => 'Администратор?',
+ 'has_two_factor' => 'Защита (2FA)?',
+ 'confirmed_from' => 'Confirmed from',
+ 'registered_from' => 'Registered from',
+ 'blocked_code' => 'Block code',
+ 'domain' => 'Домен',
+ 'registration_attempts' => 'Registration attempts',
+ 'source_account' => 'Source account',
+ 'destination_account' => 'Destination account',
+
+ 'accounts_count' => 'Всего счетов',
+ 'journals_count' => 'Всего транзакций',
+ 'attachments_count' => 'Всего вложений',
+ 'bills_count' => 'Всего счетов к оплате',
+ 'categories_count' => 'Всего категорий',
+ 'export_jobs_count' => 'Задачи по экспорту',
+ 'import_jobs_count' => 'Задачи по импорту',
+ 'budget_count' => 'Всего категорий бюджета',
+ 'rule_and_groups_count' => 'Всего правил и групп правил',
+ 'tags_count' => 'Всего меток',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/pagination.php b/resources/lang/ru_RU/pagination.php
new file mode 100644
index 0000000000..ed545dbf39
--- /dev/null
+++ b/resources/lang/ru_RU/pagination.php
@@ -0,0 +1,17 @@
+ '« Предыдущие',
+ 'next' => 'Следующие »',
+
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/passwords.php b/resources/lang/ru_RU/passwords.php
new file mode 100644
index 0000000000..79f5b974a5
--- /dev/null
+++ b/resources/lang/ru_RU/passwords.php
@@ -0,0 +1,19 @@
+ 'Пароль должен содержать не менее 6 символов. Пароль и его подтверждение должны совпадать.',
+ 'user' => 'Мы не можем найти пользователя с таким e-mail.',
+ 'token' => 'Это неправильный ключ для сброса пароля.',
+ 'sent' => 'Мы отправили ссылку для сброса пароля на ваш e-mail!',
+ 'reset' => 'Ваш пароль был успешно сброшен!',
+ 'blocked' => 'Это была хорошая попытка.',
+];
\ No newline at end of file
diff --git a/resources/lang/ru_RU/validation.php b/resources/lang/ru_RU/validation.php
new file mode 100644
index 0000000000..a51b836d9c
--- /dev/null
+++ b/resources/lang/ru_RU/validation.php
@@ -0,0 +1,91 @@
+ 'Это некорректный IBAN.',
+ 'unique_account_number_for_user' => 'Этот номер счёта уже используется.',
+ 'deleted_user' => 'По соображениям безопасности, вы не можете зарегистрироваться, используя этот адрес электронной почты.',
+ 'rule_trigger_value' => 'Это значение является недопустимым для выбранного триггера.',
+ 'rule_action_value' => 'Это значение является недопустимым для выбранного действия.',
+ 'invalid_domain' => 'По соображениям безопасности, вы не можете зарегистрироваться, используя почту на указанном домене.',
+ 'file_already_attached' => 'Загруженный файл ":name" уже прикреплён к этому объекту.',
+ 'file_attached' => 'Файл ":name". успешно загружен.',
+ 'file_invalid_mime' => 'Файл ":name" имеет тип ":mime". Загрузка файлов такого типа невозможна.',
+ 'file_too_large' => 'Файл ":name" слишком большой.',
+ 'belongs_to_user' => 'Значение :attribute неизвестно',
+ 'accepted' => 'The :attribute must be accepted.',
+ 'bic' => 'This is not a valid BIC.',
+ 'more' => ':attribute must be larger than zero.',
+ 'active_url' => 'The :attribute is not a valid URL.',
+ 'after' => 'The :attribute must be a date after :date.',
+ 'alpha' => 'The :attribute may only contain letters.',
+ 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.',
+ 'alpha_num' => 'The :attribute may only contain letters and numbers.',
+ 'array' => 'The :attribute must be an array.',
+ 'unique_for_user' => 'There already is an entry with this :attribute.',
+ 'before' => 'The :attribute must be a date before :date.',
+ 'unique_object_for_user' => 'This name is already in use',
+ 'unique_account_for_user' => 'This account name is already in use',
+ 'between.numeric' => 'The :attribute must be between :min and :max.',
+ 'between.file' => 'The :attribute must be between :min and :max kilobytes.',
+ 'between.string' => 'The :attribute must be between :min and :max characters.',
+ 'between.array' => 'The :attribute must have between :min and :max items.',
+ 'boolean' => 'The :attribute field must be true or false.',
+ 'confirmed' => 'The :attribute confirmation does not match.',
+ 'date' => 'The :attribute is not a valid date.',
+ 'date_format' => 'The :attribute does not match the format :format.',
+ 'different' => 'The :attribute and :other must be different.',
+ 'digits' => 'The :attribute must be :digits digits.',
+ 'digits_between' => 'The :attribute must be between :min and :max digits.',
+ 'email' => 'The :attribute must be a valid email address.',
+ 'filled' => 'The :attribute field is required.',
+ 'exists' => 'The selected :attribute is invalid.',
+ 'image' => 'The :attribute must be an image.',
+ 'in' => 'The selected :attribute is invalid.',
+ 'integer' => 'The :attribute must be an integer.',
+ 'ip' => 'The :attribute must be a valid IP address.',
+ 'json' => 'The :attribute must be a valid JSON string.',
+ 'max.numeric' => 'The :attribute may not be greater than :max.',
+ 'max.file' => 'The :attribute may not be greater than :max kilobytes.',
+ 'max.string' => 'The :attribute may not be greater than :max characters.',
+ 'max.array' => 'The :attribute may not have more than :max items.',
+ 'mimes' => 'The :attribute must be a file of type: :values.',
+ 'min.numeric' => 'The :attribute must be at least :min.',
+ 'min.file' => 'The :attribute must be at least :min kilobytes.',
+ 'min.string' => 'The :attribute must be at least :min characters.',
+ 'min.array' => 'The :attribute must have at least :min items.',
+ 'not_in' => 'The selected :attribute is invalid.',
+ 'numeric' => 'The :attribute must be a number.',
+ 'regex' => 'The :attribute format is invalid.',
+ 'required' => 'The :attribute field is required.',
+ 'required_if' => 'The :attribute field is required when :other is :value.',
+ 'required_unless' => 'The :attribute field is required unless :other is in :values.',
+ 'required_with' => 'The :attribute field is required when :values is present.',
+ 'required_with_all' => 'The :attribute field is required when :values is present.',
+ 'required_without' => 'The :attribute field is required when :values is not present.',
+ 'required_without_all' => 'The :attribute field is required when none of :values are present.',
+ 'same' => 'The :attribute and :other must match.',
+ 'size.numeric' => 'The :attribute must be :size.',
+ 'size.file' => 'The :attribute must be :size kilobytes.',
+ 'size.string' => 'The :attribute must be :size characters.',
+ 'size.array' => 'The :attribute must contain :size items.',
+ 'unique' => 'The :attribute has already been taken.',
+ 'string' => 'The :attribute must be a string.',
+ 'url' => 'The :attribute format is invalid.',
+ 'timezone' => 'The :attribute must be a valid zone.',
+ '2fa_code' => 'The :attribute field is invalid.',
+ 'dimensions' => 'The :attribute has invalid image dimensions.',
+ 'distinct' => 'The :attribute field has a duplicate value.',
+ 'file' => 'The :attribute must be a file.',
+ 'in_array' => 'The :attribute field does not exist in :other.',
+ 'present' => 'The :attribute field must be present.',
+ 'amount_zero' => 'The total amount cannot be zero',
+];
\ No newline at end of file
diff --git a/resources/lang/sl_SI/breadcrumbs.php b/resources/lang/sl_SI/breadcrumbs.php
index 051d41b74e..1db93e081f 100644
--- a/resources/lang/sl_SI/breadcrumbs.php
+++ b/resources/lang/sl_SI/breadcrumbs.php
@@ -24,7 +24,7 @@ return [
'edit_bill' => 'uredi trajnik ":name"',
'delete_bill' => 'izbriši trajnik ":name"',
'reports' => 'Poročila',
- 'searchResult' => 'rezultati iskanja za ":query"',
+ 'search_result' => 'Search results for ":query"',
'withdrawal_list' => 'stroški',
'deposit_list' => 'prihodki',
'transfer_list' => 'prenosi',
diff --git a/resources/lang/sl_SI/firefly.php b/resources/lang/sl_SI/firefly.php
index c80bec6d26..7e1f82ef74 100644
--- a/resources/lang/sl_SI/firefly.php
+++ b/resources/lang/sl_SI/firefly.php
@@ -27,8 +27,7 @@ return [
'showEverything' => 'pokaži vse',
'never' => 'nikoli',
'search_results_for' => 'rezultati iskanja za ":query"',
- 'advanced_search' => 'napredno iskanje',
- 'advanced_search_intro' => 'There are several modifiers that you can use in your search to narrow down the results. If you use any of these, the search will only return transactions. Please click the -icon for more information.',
+ 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.',
'bounced_error' => 'The message sent to :email bounced, so no access for you.',
'deleted_error' => 'These credentials do not match our records.',
'general_blocked_error' => 'Your account has been disabled, so you cannot login.',
@@ -69,13 +68,6 @@ return [
'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.',
'warning_much_data' => ':days days of data may take a while to load.',
'registered' => 'You have registered successfully!',
- 'search' => 'Search',
- 'search_found_accounts' => 'Found :count account(s) for your query.',
- 'search_found_categories' => 'Found :count category(ies) for your query.',
- 'search_found_budgets' => 'Found :count budget(s) for your query.',
- 'search_found_tags' => 'Found :count tag(s) for your query.',
- 'search_found_transactions' => 'Found :count transaction(s) for your query.',
- 'results_limited' => 'The results are limited to :count entries.',
'tagbalancingAct' => 'Balancing act',
'tagadvancePayment' => 'Advance payment',
'tagnothing' => '',
@@ -88,6 +80,8 @@ return [
'user_id_is' => 'Your user id is :user',
'field_supports_markdown' => 'This field supports Markdown.',
'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.',
+ 'reenable_intro_text' => 'You can also reenable the introduction guidance.',
+ 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.',
'nothing_to_display' => 'There are no transactions to show you',
'show_all_no_filter' => 'Show all transactions without grouping them by date.',
'expenses_by_category' => 'stroški po kategorijah',
@@ -152,6 +146,15 @@ return [
'transfer_exchange_rate_instructions' => 'Izvorni premoženjski račun "@source_name" sprejema samo transakcije v @source_currency. Ciljni premoženjski račun "@dest_name" sprejema samo transakcije v @dest_currency. Podati morate znesek v obeh valutah.',
'transaction_data' => 'Transaction data',
+ // search
+ 'search' => 'Search',
+ 'search_found_transactions' => 'Number of transactions found:',
+ 'general_search_error' => 'An error occured while searching. Please check the log files for more information.',
+ 'search_box' => 'Search',
+ 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.',
+ 'search_error' => 'Error while searching',
+ 'search_searching' => 'Searching ...',
+
// repeat frequencies:
'repeat_freq_yearly' => 'yearly',
'repeat_freq_monthly' => 'monthly',
@@ -204,7 +207,6 @@ return [
// rules
'rules' => 'Rules',
- 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.',
'rule_name' => 'Name of rule',
'rule_triggers' => 'Rule triggers when',
'rule_actions' => 'Rule will',
@@ -254,14 +256,14 @@ return [
'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions',
'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.',
'warning_no_valid_triggers' => 'No valid triggers provided.',
- 'execute_on_existing_transactions' => 'Execute for existing transactions',
- 'rule_group_select_transactions' => 'Execute rule group ":title" on existing transactions',
- 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions',
- 'execute_on_existing_transactions_short' => 'Existing transactions',
- 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions',
- 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions',
+ 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions',
+ 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.',
'include_transactions_from_accounts' => 'Include transactions from these accounts',
+ 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.',
'execute' => 'Execute',
+ 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions',
+ 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.',
+ 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.',
// actions and triggers
'rule_trigger_user_action' => 'User action is ":trigger_value"',
@@ -426,12 +428,6 @@ return [
'attachment_updated' => 'Updated attachment ":name"',
'upload_max_file_size' => 'Maximum file size: :size',
- // tour:
- 'prev' => 'Prev',
- 'next' => 'Next',
- 'end-tour' => 'End tour',
- 'pause' => 'Pause',
-
// transaction index
'title_expenses' => 'stroški',
'title_withdrawal' => 'stroški',
@@ -499,15 +495,6 @@ return [
'make_default_currency' => 'make default',
'default_currency' => 'default',
- // new user:
- 'submit' => 'Submit',
- 'getting_started' => 'Getting started',
- 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:',
- 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:',
- 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.',
- 'stored_new_account_new_user' => 'Yay! Your new account has been stored.',
- 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.',
-
// forms:
'mandatoryFields' => 'Mandatory fields',
'optionalFields' => 'Optional fields',
@@ -556,6 +543,8 @@ return [
'average_bill_amount_overall' => 'Average bill amount (overall)',
'not_or_not_yet' => 'Not (yet)',
'not_expected_period' => 'Not expected this period',
+ 'bill_is_active' => 'Bill is active',
+ 'bill_will_automatch' => 'Bill will automatically linked to matching transactions',
// accounts:
'details_for_asset' => 'podrobnosti za premoženjski račun ":name"',
'details_for_expense' => 'podrobnosti stroškovnega konta ":name"',
@@ -643,6 +632,12 @@ return [
// new user:
'welcome' => 'Welcome to Firefly!',
+ 'submit' => 'Submit',
+ 'getting_started' => 'Getting started',
+ 'to_get_started' => 'It is good to see you have successfully installed Firefly III. To get started with this tool please enter your bank\'s name and the balance of your main checking account. Do not worry yet if you have multiple accounts. You can add those later. It\'s just that Firefly III needs something to start with.',
+ 'savings_balance_text' => 'Firefly III will automatically create a savings account for you. By default, there will be no money in your savings account, but if you tell Firefly III the balance it will be stored as such.',
+ 'finish_up_new_user' => 'That\'s it! You can continue by pressing Submit. You will be taken to the index of Firefly III.',
+ 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.',
// home page:
'yourAccounts' => 'Your accounts',
diff --git a/resources/lang/sl_SI/intro.php b/resources/lang/sl_SI/intro.php
new file mode 100644
index 0000000000..7fb42114a8
--- /dev/null
+++ b/resources/lang/sl_SI/intro.php
@@ -0,0 +1,118 @@
+ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.',
+ 'index_accounts-chart' => 'This chart shows the current balance of your asset accounts. You can select the accounts visible here in your preferences.',
+ 'index_box_out_holder' => 'This little box and the boxes next to this one will give you a quick overview of your financial situation.',
+ 'index_help' => 'If you ever need help with a page or a form, press this button.',
+ 'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
+ 'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
+
+ // create account:
+ 'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
+ 'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly.',
+ 'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
+ 'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
+
+ // budgets index
+ 'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
+ 'budgets_index_set_budget' => 'Set your total budget for every period so Firefly can tell you if you have budgeted all available money.',
+ 'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
+ 'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
+ 'budgets_index_new_budget' => 'Create new budgets as you see fit.',
+ 'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
+
+ // reports (index)
+ 'reports_index_intro' => 'Use these reports to get detailed insights in your finances.',
+ 'reports_index_inputReportType' => 'Pick a report type. Check out the help pages to see what each report shows you.',
+ 'reports_index_inputAccountsSelect' => 'You can exclude or include asset accounts as you see fit.',
+ 'reports_index_inputDateRange' => 'The selected date range is entirely up to you: from one day to 10 years.',
+ 'reports_index_extra-options-box' => 'Depending on the report you have selected, you can select extra filters and options here. Watch this box when you change report types.',
+
+ // reports (reports)
+ 'reports_report_default_intro' => 'This report will give you a quick and comprehensive overview of your finances. If you wish to see anything else, please don\'t hestitate to contact me!',
+ 'reports_report_audit_intro' => 'This report will give you detailed insights in your asset accounts.',
+ 'reports_report_audit_optionsBox' => 'Use these check boxes to show or hide the columns you are interested in.',
+
+ 'reports_report_category_intro' => 'This report will give you insight in or multiple categories.',
+ 'reports_report_category_pieCharts' => 'These charts will give you insight in expenses and income per category or per account.',
+ 'reports_report_category_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per category.',
+
+ 'reports_report_tag_intro' => 'This report will give you insight in or multiple tags.',
+ 'reports_report_tag_pieCharts' => 'These charts will give you insight in expenses and income per tag, account, category or budget.',
+ 'reports_report_tag_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per tag.',
+
+ 'reports_report_budget_intro' => 'This report will give you insight in or multiple budgets.',
+ 'reports_report_budget_pieCharts' => 'These charts will give you insight in expenses and income per budget or per account.',
+ 'reports_report_budget_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per budget.',
+
+ // create transaction
+ 'transactions_create_switch_box' => 'Use these buttons to quickly switch the type of transaction you wish to save.',
+ 'transactions_create_ffInput_category' => 'You can freely type in this field. Previously created categories will be suggested.',
+ 'transactions_create_withdrawal_ffInput_budget' => 'Link your withdrawal to a budget for better financial control.',
+ 'transactions_create_withdrawal_currency_dropdown_amount' => 'Use this dropdown when your withdrawal is in another currency.',
+ 'transactions_create_deposit_currency_dropdown_amount' => 'Use this dropdown when your deposit is in another currency.',
+ 'transactions_create_transfer_ffInput_piggy_bank_id' => 'Select a piggy bank and link this transfer to your savings.',
+
+ // piggy banks index:
+ 'piggy-banks_index_saved' => 'This field shows you how much you\'ve saved in each piggy bank.',
+ 'piggy-banks_index_button' => 'Next to this progress bar are two buttons (+ and -) to add or remove money from each piggy bank.',
+ 'piggy-banks_index_accountStatus' => 'For each asset account with at least one piggy bank the status is listed in this table.',
+
+ // create piggy
+ 'piggy-banks_create_name' => 'What is your goal? A new couch, a camera, money for emergencies?',
+ 'piggy-banks_create_date' => 'You can set a target date or a deadline for your piggy bank.',
+
+ // show piggy
+ 'piggy-banks_show_piggyChart' => 'This chart will show the history of this piggy bank.',
+ 'piggy-banks_show_piggyDetails' => 'Some details about your piggy bank',
+ 'piggy-banks_show_piggyEvents' => 'Any additions or removals are also listed here.',
+
+ // bill index
+ 'bills_index_paid_in_period' => 'This field indicates when the bill was last paid.',
+ 'bills_index_expected_in_period' => 'This field indicates for each bill if and when the next bill is expected to hit.',
+
+ // show bill
+ 'bills_show_billInfo' => 'This table shows some general information about this bill.',
+ 'bills_show_billButtons' => 'Use this button to re-scan old transactions so they will be matched to this bill.',
+ 'bills_show_billChart' => 'This chart shows the transactions linked to this bill.',
+
+ // create bill
+ 'bills_create_name' => 'Use a descriptive name such as "Rent" or "Health insurance".',
+ 'bills_create_match' => 'To match transactions, use terms from those transactions or the expense account involved. All words must match.',
+ 'bills_create_amount_min_holder' => 'Select a minimum and maximum amount for this bill.',
+ 'bills_create_repeat_freq_holder' => 'Most bills repeat monthly, but you can set another frequency here.',
+ 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks for example, the "skip"-field should be set to "1" to skip every other week.',
+
+ // rules index
+ 'rules_index_intro' => 'Firefly III allows you to manage rules, that will automagically be applied to any transaction you create or edit.',
+ 'rules_index_new_rule_group' => 'You can combine rules in groups for easier management.',
+ 'rules_index_new_rule' => 'Create as many rules as you like.',
+ 'rules_index_prio_buttons' => 'Order them any way you see fit.',
+ 'rules_index_test_buttons' => 'You can test your rules or apply them to existing transactions.',
+ 'rules_index_rule-triggers' => 'Rules have "triggers" and "actions" that you can order by drag-and-drop.',
+ 'rules_index_outro' => 'Be sure to check out the help pages using the (?) icon in the top right!',
+
+ // create rule:
+ 'rules_create_mandatory' => 'Choose a descriptive title, and set when the rule should be fired.',
+ 'rules_create_ruletriggerholder' => 'Add as many triggers as you like, but remember that ALL triggers must match before any actions are fired.',
+ 'rules_create_test_rule_triggers' => 'Use this button to see which transactions would match your rule.',
+ 'rules_create_actions' => 'Set as many actions as you like.',
+
+ // preferences
+ 'preferences_index_tabs' => 'More options are available behind these tabs.',
+
+ // currencies
+ 'currencies_index_default' => 'Firefly III has one default currency. You can always switch of course using these buttons.',
+
+ // create currency
+ 'currencies_create_code' => 'This code should be ISO compliant (Google it for your new currency).',
+];
\ No newline at end of file
diff --git a/resources/lang/zh_TW/breadcrumbs.php b/resources/lang/zh_TW/breadcrumbs.php
index d9fe1aac5b..a2271c14b2 100644
--- a/resources/lang/zh_TW/breadcrumbs.php
+++ b/resources/lang/zh_TW/breadcrumbs.php
@@ -24,7 +24,7 @@ return [
'edit_bill' => '編輯賬單 ":name"',
'delete_bill' => '刪除賬單 ":name"',
'reports' => '報表',
- 'searchResult' => '搜尋 ":query"',
+ 'search_result' => 'Search results for ":query"',
'withdrawal_list' => '支出',
'deposit_list' => '收入、薪金與存款',
'transfer_list' => '轉帳',
diff --git a/resources/lang/zh_TW/firefly.php b/resources/lang/zh_TW/firefly.php
index be0cab5ac3..c7a2562562 100644
--- a/resources/lang/zh_TW/firefly.php
+++ b/resources/lang/zh_TW/firefly.php
@@ -27,8 +27,7 @@ return [
'showEverything' => '全部顯示',
'never' => '從來沒有',
'search_results_for' => '":query" 的搜尋結果',
- 'advanced_search' => 'Advanced search',
- 'advanced_search_intro' => 'There are several modifiers that you can use in your search to narrow down the results. If you use any of these, the search will only return transactions. Please click the -icon for more information.',
+ 'no_results_for_empty_search' => 'Your search was empty, so nothing was found.',
'bounced_error' => '無法傳送電郵至 :email ,因此無法訪問。',
'deleted_error' => '帳號或密碼錯誤。',
'general_blocked_error' => '您的帳戶已被禁用,所以您不能登錄。',
@@ -69,13 +68,6 @@ return [
'two_factor_lost_fix_owner' => '否則,請電郵網站擁有者,:site_owner 並要求他們重置你的雙重身份驗證。',
'warning_much_data' => ':days 天的資料需要一點時間載入。',
'registered' => '您已成功註冊 !',
- 'search' => '搜尋',
- 'search_found_accounts' => 'Found :count account(s) for your query.',
- 'search_found_categories' => 'Found :count category(ies) for your query.',
- 'search_found_budgets' => 'Found :count budget(s) for your query.',
- 'search_found_tags' => 'Found :count tag(s) for your query.',
- 'search_found_transactions' => 'Found :count transaction(s) for your query.',
- 'results_limited' => 'The results are limited to :count entries.',
'tagbalancingAct' => 'Balancing act',
'tagadvancePayment' => 'Advance payment',
'tagnothing' => '',
@@ -88,6 +80,8 @@ return [
'user_id_is' => 'Your user id is :user',
'field_supports_markdown' => 'This field supports Markdown.',
'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.',
+ 'reenable_intro_text' => 'You can also reenable the introduction guidance.',
+ 'intro_boxes_after_refresh' => 'The introduction boxes will reappear when you refresh the page.',
'nothing_to_display' => 'There are no transactions to show you',
'show_all_no_filter' => 'Show all transactions without grouping them by date.',
'expenses_by_category' => 'Expenses by category',
@@ -152,6 +146,15 @@ return [
'transfer_exchange_rate_instructions' => 'Source asset account "@source_name" only accepts transactions in @source_currency. Destination asset account "@dest_name" only accepts transactions in @dest_currency. You must provide the transferred amount correctly in both currencies.',
'transaction_data' => 'Transaction data',
+ // search
+ 'search' => '搜尋',
+ 'search_found_transactions' => 'Number of transactions found:',
+ 'general_search_error' => 'An error occured while searching. Please check the log files for more information.',
+ 'search_box' => 'Search',
+ 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.',
+ 'search_error' => 'Error while searching',
+ 'search_searching' => 'Searching ...',
+
// repeat frequencies:
'repeat_freq_yearly' => 'yearly',
'repeat_freq_monthly' => '每月',
@@ -204,7 +207,6 @@ return [
// rules
'rules' => 'Rules',
- 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.',
'rule_name' => 'Name of rule',
'rule_triggers' => 'Rule triggers when',
'rule_actions' => 'Rule will',
@@ -254,14 +256,14 @@ return [
'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions',
'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.',
'warning_no_valid_triggers' => 'No valid triggers provided.',
- 'execute_on_existing_transactions' => 'Execute for existing transactions',
- 'rule_group_select_transactions' => 'Execute rule group ":title" on existing transactions',
- 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions',
- 'execute_on_existing_transactions_short' => 'Existing transactions',
- 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions',
- 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions',
+ 'apply_rule_selection' => 'Apply rule ":title" to a selection of your transactions',
+ 'apply_rule_selection_intro' => 'Rules like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run it on a selection of your existing transactions. This can be useful when you have updated a rule and you need the changes to be applied to all of your other transactions.',
'include_transactions_from_accounts' => 'Include transactions from these accounts',
+ 'applied_rule_selection' => 'Rule ":title" has been applied to your selection.',
'execute' => 'Execute',
+ 'apply_rule_group_selection' => 'Apply rule group ":title" to a selection of your transactions',
+ 'apply_rule_group_selection_intro' => 'Rule groups like ":title" are normally only applied to new or updated transactions, but you can tell Firefly III to run all the rules in this group on a selection of your existing transactions. This can be useful when you have updated a group of rules and you need the changes to be applied to all of your other transactions.',
+ 'applied_rule_group_selection' => 'Rule group ":title" has been applied to your selection.',
// actions and triggers
'rule_trigger_user_action' => 'User action is ":trigger_value"',
@@ -426,12 +428,6 @@ return [
'attachment_updated' => 'Updated attachment ":name"',
'upload_max_file_size' => 'Maximum file size: :size',
- // tour:
- 'prev' => 'Prev',
- 'next' => 'Next',
- 'end-tour' => 'End tour',
- 'pause' => 'Pause',
-
// transaction index
'title_expenses' => 'Expenses',
'title_withdrawal' => 'Expenses',
@@ -499,15 +495,6 @@ return [
'make_default_currency' => 'make default',
'default_currency' => 'default',
- // new user:
- 'submit' => 'Submit',
- 'getting_started' => 'Getting started',
- 'to_get_started' => '請輸入你的銀行名稱和帳戶餘額,以開始使用 Firefly︰',
- 'savings_balance_text' => '如果你有儲蓄帳戶的話,請輸入你的儲蓄帳戶當前的餘額︰',
- 'cc_balance_text' => '如果你有信用卡,請輸入你的信用卡限額。',
- 'stored_new_account_new_user' => 'Yay! Your new account has been stored.',
- 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.',
-
// forms:
'mandatoryFields' => 'Mandatory fields',
'optionalFields' => 'Optional fields',
@@ -556,6 +543,8 @@ return [
'average_bill_amount_overall' => '平均賬單金額 (總計)',
'not_or_not_yet' => 'Not (yet)',
'not_expected_period' => 'Not expected this period',
+ 'bill_is_active' => 'Bill is active',
+ 'bill_will_automatch' => 'Bill will automatically linked to matching transactions',
// accounts:
'details_for_asset' => '資產帳戶的詳細資訊 ":name"',
'details_for_expense' => 'Details for expense account ":name"',
@@ -643,6 +632,12 @@ return [
// new user:
'welcome' => 'Welcome to Firefly!',
+ 'submit' => 'Submit',
+ 'getting_started' => 'Getting started',
+ 'to_get_started' => 'It is good to see you have successfully installed Firefly III. To get started with this tool please enter your bank\'s name and the balance of your main checking account. Do not worry yet if you have multiple accounts. You can add those later. It\'s just that Firefly III needs something to start with.',
+ 'savings_balance_text' => 'Firefly III will automatically create a savings account for you. By default, there will be no money in your savings account, but if you tell Firefly III the balance it will be stored as such.',
+ 'finish_up_new_user' => 'That\'s it! You can continue by pressing Submit. You will be taken to the index of Firefly III.',
+ 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.',
// home page:
'yourAccounts' => 'Your accounts',
diff --git a/resources/lang/zh_TW/intro.php b/resources/lang/zh_TW/intro.php
new file mode 100644
index 0000000000..7fb42114a8
--- /dev/null
+++ b/resources/lang/zh_TW/intro.php
@@ -0,0 +1,118 @@
+ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.',
+ 'index_accounts-chart' => 'This chart shows the current balance of your asset accounts. You can select the accounts visible here in your preferences.',
+ 'index_box_out_holder' => 'This little box and the boxes next to this one will give you a quick overview of your financial situation.',
+ 'index_help' => 'If you ever need help with a page or a form, press this button.',
+ 'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
+ 'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
+
+ // create account:
+ 'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
+ 'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly.',
+ 'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
+ 'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
+
+ // budgets index
+ 'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
+ 'budgets_index_set_budget' => 'Set your total budget for every period so Firefly can tell you if you have budgeted all available money.',
+ 'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
+ 'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
+ 'budgets_index_new_budget' => 'Create new budgets as you see fit.',
+ 'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
+
+ // reports (index)
+ 'reports_index_intro' => 'Use these reports to get detailed insights in your finances.',
+ 'reports_index_inputReportType' => 'Pick a report type. Check out the help pages to see what each report shows you.',
+ 'reports_index_inputAccountsSelect' => 'You can exclude or include asset accounts as you see fit.',
+ 'reports_index_inputDateRange' => 'The selected date range is entirely up to you: from one day to 10 years.',
+ 'reports_index_extra-options-box' => 'Depending on the report you have selected, you can select extra filters and options here. Watch this box when you change report types.',
+
+ // reports (reports)
+ 'reports_report_default_intro' => 'This report will give you a quick and comprehensive overview of your finances. If you wish to see anything else, please don\'t hestitate to contact me!',
+ 'reports_report_audit_intro' => 'This report will give you detailed insights in your asset accounts.',
+ 'reports_report_audit_optionsBox' => 'Use these check boxes to show or hide the columns you are interested in.',
+
+ 'reports_report_category_intro' => 'This report will give you insight in or multiple categories.',
+ 'reports_report_category_pieCharts' => 'These charts will give you insight in expenses and income per category or per account.',
+ 'reports_report_category_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per category.',
+
+ 'reports_report_tag_intro' => 'This report will give you insight in or multiple tags.',
+ 'reports_report_tag_pieCharts' => 'These charts will give you insight in expenses and income per tag, account, category or budget.',
+ 'reports_report_tag_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per tag.',
+
+ 'reports_report_budget_intro' => 'This report will give you insight in or multiple budgets.',
+ 'reports_report_budget_pieCharts' => 'These charts will give you insight in expenses and income per budget or per account.',
+ 'reports_report_budget_incomeAndExpensesChart' => 'This chart shows you how your expenses and income per budget.',
+
+ // create transaction
+ 'transactions_create_switch_box' => 'Use these buttons to quickly switch the type of transaction you wish to save.',
+ 'transactions_create_ffInput_category' => 'You can freely type in this field. Previously created categories will be suggested.',
+ 'transactions_create_withdrawal_ffInput_budget' => 'Link your withdrawal to a budget for better financial control.',
+ 'transactions_create_withdrawal_currency_dropdown_amount' => 'Use this dropdown when your withdrawal is in another currency.',
+ 'transactions_create_deposit_currency_dropdown_amount' => 'Use this dropdown when your deposit is in another currency.',
+ 'transactions_create_transfer_ffInput_piggy_bank_id' => 'Select a piggy bank and link this transfer to your savings.',
+
+ // piggy banks index:
+ 'piggy-banks_index_saved' => 'This field shows you how much you\'ve saved in each piggy bank.',
+ 'piggy-banks_index_button' => 'Next to this progress bar are two buttons (+ and -) to add or remove money from each piggy bank.',
+ 'piggy-banks_index_accountStatus' => 'For each asset account with at least one piggy bank the status is listed in this table.',
+
+ // create piggy
+ 'piggy-banks_create_name' => 'What is your goal? A new couch, a camera, money for emergencies?',
+ 'piggy-banks_create_date' => 'You can set a target date or a deadline for your piggy bank.',
+
+ // show piggy
+ 'piggy-banks_show_piggyChart' => 'This chart will show the history of this piggy bank.',
+ 'piggy-banks_show_piggyDetails' => 'Some details about your piggy bank',
+ 'piggy-banks_show_piggyEvents' => 'Any additions or removals are also listed here.',
+
+ // bill index
+ 'bills_index_paid_in_period' => 'This field indicates when the bill was last paid.',
+ 'bills_index_expected_in_period' => 'This field indicates for each bill if and when the next bill is expected to hit.',
+
+ // show bill
+ 'bills_show_billInfo' => 'This table shows some general information about this bill.',
+ 'bills_show_billButtons' => 'Use this button to re-scan old transactions so they will be matched to this bill.',
+ 'bills_show_billChart' => 'This chart shows the transactions linked to this bill.',
+
+ // create bill
+ 'bills_create_name' => 'Use a descriptive name such as "Rent" or "Health insurance".',
+ 'bills_create_match' => 'To match transactions, use terms from those transactions or the expense account involved. All words must match.',
+ 'bills_create_amount_min_holder' => 'Select a minimum and maximum amount for this bill.',
+ 'bills_create_repeat_freq_holder' => 'Most bills repeat monthly, but you can set another frequency here.',
+ 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks for example, the "skip"-field should be set to "1" to skip every other week.',
+
+ // rules index
+ 'rules_index_intro' => 'Firefly III allows you to manage rules, that will automagically be applied to any transaction you create or edit.',
+ 'rules_index_new_rule_group' => 'You can combine rules in groups for easier management.',
+ 'rules_index_new_rule' => 'Create as many rules as you like.',
+ 'rules_index_prio_buttons' => 'Order them any way you see fit.',
+ 'rules_index_test_buttons' => 'You can test your rules or apply them to existing transactions.',
+ 'rules_index_rule-triggers' => 'Rules have "triggers" and "actions" that you can order by drag-and-drop.',
+ 'rules_index_outro' => 'Be sure to check out the help pages using the (?) icon in the top right!',
+
+ // create rule:
+ 'rules_create_mandatory' => 'Choose a descriptive title, and set when the rule should be fired.',
+ 'rules_create_ruletriggerholder' => 'Add as many triggers as you like, but remember that ALL triggers must match before any actions are fired.',
+ 'rules_create_test_rule_triggers' => 'Use this button to see which transactions would match your rule.',
+ 'rules_create_actions' => 'Set as many actions as you like.',
+
+ // preferences
+ 'preferences_index_tabs' => 'More options are available behind these tabs.',
+
+ // currencies
+ 'currencies_index_default' => 'Firefly III has one default currency. You can always switch of course using these buttons.',
+
+ // create currency
+ 'currencies_create_code' => 'This code should be ISO compliant (Google it for your new currency).',
+];
\ No newline at end of file
diff --git a/resources/views/accounts/index.twig b/resources/views/accounts/index.twig
index e40e3984a3..230e3a3a59 100644
--- a/resources/views/accounts/index.twig
+++ b/resources/views/accounts/index.twig
@@ -11,8 +11,6 @@