diff --git a/.deploy/docker/entrypoint.sh b/.deploy/docker/entrypoint.sh index 17d62270c3..e88cc8cf85 100755 --- a/.deploy/docker/entrypoint.sh +++ b/.deploy/docker/entrypoint.sh @@ -1,8 +1,23 @@ #!/bin/bash +# make sure the correct directories exists (suggested by @chrif): +mkdir -p $FIREFLY_PATH/storage/app +mkdir -p $FIREFLY_PATH/storage/app/public +mkdir -p $FIREFLY_PATH/storage/build +mkdir -p $FIREFLY_PATH/storage/database +mkdir -p $FIREFLY_PATH/storage/debugbar +mkdir -p $FIREFLY_PATH/storage/export +mkdir -p $FIREFLY_PATH/storage/framework/cache +mkdir -p $FIREFLY_PATH/storage/framework/sessions +mkdir -p $FIREFLY_PATH/storage/framework/testing +mkdir -p $FIREFLY_PATH/storage/framework/views +mkdir -p $FIREFLY_PATH/storage/logs +mkdir -p $FIREFLY_PATH/storage/upload + + # make sure we own the volumes: -chown -R www-data:www-data -R $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/cache -chmod -R 775 $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/cache +chown -R www-data:www-data -R $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/framework/cache +chmod -R 775 $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/framework/cache # remove any lingering files that may break upgrades: rm -f $FIREFLY_PATH/storage/logs/laravel.log diff --git a/.env.docker b/.env.docker index 20f4436f77..502b81ef49 100644 --- a/.env.docker +++ b/.env.docker @@ -50,6 +50,7 @@ COOKIE_DOMAIN= COOKIE_SECURE=false # If you want Firefly III to mail you, update these settings +# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html MAIL_DRIVER=${MAIL_DRIVER} MAIL_HOST=${MAIL_HOST} MAIL_PORT=${MAIL_PORT} @@ -58,6 +59,12 @@ MAIL_USERNAME=${MAIL_USERNAME} MAIL_PASSWORD=${MAIL_PASSWORD} MAIL_ENCRYPTION=${MAIL_ENCRYPTION} +# Other mail drivers: +MAILGUN_DOMAIN=${MAILGUN_DOMAIN} +MAILGUN_SECRET=${MAILGUN_SECRET} +MANDRILL_SECRET=${MANDRILL_SECRET} +SPARKPOST_SECRET=${SPARKPOST_SECRET} + # Firefly III can send you the following messages SEND_REGISTRATION_MAIL=true SEND_ERROR_MESSAGE=false diff --git a/.env.example b/.env.example index e45753a2ca..73afaf0470 100644 --- a/.env.example +++ b/.env.example @@ -54,6 +54,7 @@ COOKIE_DOMAIN= COOKIE_SECURE=false # If you want Firefly III to mail you, update these settings +# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html MAIL_DRIVER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 @@ -62,6 +63,12 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +# Other mail drivers: +MAILGUN_DOMAIN= +MAILGUN_SECRET= +MANDRILL_SECRET= +SPARKPOST_SECRET= + # Firefly III can send you the following messages SEND_REGISTRATION_MAIL=true SEND_ERROR_MESSAGE=true @@ -100,3 +107,5 @@ IS_DOCKER=false IS_SANDSTORM=false BUNQ_USE_SANDBOX=false IS_HEROKU=false +MAILGUN_DOMAIN= +MAILGUN_SECRET= diff --git a/.env.heroku b/.env.heroku index 91e273659a..55237bd98b 100644 --- a/.env.heroku +++ b/.env.heroku @@ -54,6 +54,7 @@ COOKIE_DOMAIN= COOKIE_SECURE=false # If you want Firefly III to mail you, update these settings +# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html MAIL_DRIVER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 @@ -62,6 +63,12 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +# Other mail drivers: +MAILGUN_DOMAIN= +MAILGUN_SECRET= +MANDRILL_SECRET= +SPARKPOST_SECRET= + # Firefly III can send you the following messages SEND_REGISTRATION_MAIL=true SEND_ERROR_MESSAGE=true @@ -100,3 +107,5 @@ IS_DOCKER=false IS_SANDSTORM=false BUNQ_USE_SANDBOX=false IS_HEROKU=true +MAILGUN_DOMAIN= +MAILGUN_SECRET= diff --git a/.env.sandstorm b/.env.sandstorm index c72676704c..5895f63467 100755 --- a/.env.sandstorm +++ b/.env.sandstorm @@ -54,6 +54,7 @@ COOKIE_DOMAIN= COOKIE_SECURE=false # If you want Firefly III to mail you, update these settings +# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html MAIL_DRIVER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 @@ -62,6 +63,12 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +# Other mail drivers: +MAILGUN_DOMAIN= +MAILGUN_SECRET= +MANDRILL_SECRET= +SPARKPOST_SECRET= + # Firefly III can send you the following messages SEND_REGISTRATION_MAIL=true SEND_ERROR_MESSAGE=true @@ -100,3 +107,5 @@ IS_DOCKER=false IS_SANDSTORM=true BUNQ_USE_SANDBOX=false IS_HEROKU=false +MAILGUN_DOMAIN= +MAILGUN_SECRET= diff --git a/.env.testing b/.env.testing index 943ab13502..6eec082239 100644 --- a/.env.testing +++ b/.env.testing @@ -49,6 +49,7 @@ COOKIE_DOMAIN= COOKIE_SECURE=false # If you want Firefly III to mail you, update these settings +# For instructions, see: https://firefly-iii.readthedocs.io/en/latest/installation/mail.html MAIL_DRIVER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 @@ -57,9 +58,11 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null -# Firefly III can send you the following messages -SEND_REGISTRATION_MAIL=true -SEND_ERROR_MESSAGE=false +# Other mail drivers: +MAILGUN_DOMAIN= +MAILGUN_SECRET= +MANDRILL_SECRET= +SPARKPOST_SECRET= # Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places. @@ -96,3 +99,5 @@ IS_DOCKER=false IS_SANDSTORM=false BUNQ_USE_SANDBOX=true IS_HEROKU=false +MAILGUN_DOMAIN= +MAILGUN_SECRET= diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 1312de943a..12ba47695e 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -13,7 +13,7 @@ I am running Firefly III version x.x.x What do you need to do to trigger this bug? **Extra info** -Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom). +Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). **Bonus points** Earn bonus points by: diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index f943c935cb..0db426cdf3 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -1,25 +1,27 @@ --- name: I have a question or a problem -about: Ask away +about: Ask away! --- I am running Firefly III version x.x.x -**Description of my issue:** +**Description** + **Steps to reproduce** +(if relevant of course) + -(please include if this problem also exists on the demo site: https://demo.firefly-iii.org/ ) **Extra info** - Please add extra info here, such as OS, browser, and the output from the `/debug`-page of your Firefly III installation (click the version at the bottom). + + **Bonus points** Earn bonus points by: -- Post a stacktrace from your log files - Add a screenshot -- Post nginx or Apache configuration \ No newline at end of file +- Replicate the problem on the demo site https://demo.firefly-iii.org/ \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 7dfae270ae..3bdd3d861e 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -8,7 +8,8 @@ about: Suggest an idea or feature for Firefly III Please describe your feature request: - I would like Firefly III to do X. -- What if you would add Y? +- What if you would add feature Y? +- Firefly III doesn't do Z. **Solution** Describe what your feature would add to Firefly III. diff --git a/.github/stale.yml b/.github/stale.yml index f9e5be7b89..8f9da31fcd 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 30 +daysUntilStale: 14 # Number of days of inactivity before a stale Issue or Pull Request is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. @@ -12,6 +12,7 @@ daysUntilClose: 7 exemptLabels: - enhancement - feature + - bug # Set to true to ignore issues in a project (defaults to false) exemptProjects: false diff --git a/.sandstorm/changelog.md b/.sandstorm/changelog.md index c46086da90..c6415e473d 100644 --- a/.sandstorm/changelog.md +++ b/.sandstorm/changelog.md @@ -1,3 +1,22 @@ +# 4.7.5 +- A new feature called "recurring transactions" that will make Firefly III automatically create transactions for you. +- New API end points for attachments, available budgets, budgets, budget limits, categories, configuration, currency exchange rates, journal links, link types, piggy banks, preferences, recurring transactions, rules, rule groups and tags. +- Added support for YunoHost. +- The 2FA secret is visible so you can type it into 2FA apps. +- Bunq and Spectre imports will now ask to apply rules. +- Sandstorm users can now make API keys. +- Various typo's in the English translations. [issue 1493](https://github.com/firefly-iii/firefly-iii/issues/1493) +- Bug where Spectre was never called [issue 1492](https://github.com/firefly-iii/firefly-iii/issues/1492) +- Clear cache after journal is created through API [issue 1483](https://github.com/firefly-iii/firefly-iii/issues/1483) +- Make sure docker directories exist [issue 1500](https://github.com/firefly-iii/firefly-iii/issues/1500) +- Broken link to bill edit [issue 1505](https://github.com/firefly-iii/firefly-iii/issues/1505) +- Several bugs in the editing of split transactions [issue 1509](https://github.com/firefly-iii/firefly-iii/issues/1509) +- Import routine ignored formatting of several date fields [issue 1510](https://github.com/firefly-iii/firefly-iii/issues/1510) +- Piggy bank events now show the correct currency [issue 1446](https://github.com/firefly-iii/firefly-iii/issues/1446) +- Inactive accounts are no longer suggested [issue 1463](https://github.com/firefly-iii/firefly-iii/issues/1463) +- Some income / expense charts are less confusing [issue 1518](https://github.com/firefly-iii/firefly-iii/issues/1518) +- Validation bug in multi-currency create view [issue 1521](https://github.com/firefly-iii/firefly-iii/issues/1521) + # 4.7.4 - [Issue 1409](https://github.com/firefly-iii/firefly-iii/issues/1409), add Indian Rupee and explain that users can do this themselves [issue 1413](https://github.com/firefly-iii/firefly-iii/issues/1413) - [Issue 1445](https://github.com/firefly-iii/firefly-iii/issues/1445), upgrade Curl in Docker image. diff --git a/.sandstorm/sandstorm-files.list b/.sandstorm/sandstorm-files.list index 06a302593d..d311a31fcf 100644 --- a/.sandstorm/sandstorm-files.list +++ b/.sandstorm/sandstorm-files.list @@ -244,10 +244,12 @@ opt/app/app/Api/V1/Controllers/AboutController.php opt/app/app/Api/V1/Controllers/AccountController.php opt/app/app/Api/V1/Controllers/BillController.php opt/app/app/Api/V1/Controllers/Controller.php +opt/app/app/Api/V1/Controllers/CurrencyController.php opt/app/app/Api/V1/Controllers/TransactionController.php opt/app/app/Api/V1/Controllers/UserController.php opt/app/app/Api/V1/Requests/AccountRequest.php opt/app/app/Api/V1/Requests/BillRequest.php +opt/app/app/Api/V1/Requests/CurrencyRequest.php opt/app/app/Api/V1/Requests/Request.php opt/app/app/Api/V1/Requests/TransactionRequest.php opt/app/app/Api/V1/Requests/UserRequest.php @@ -321,6 +323,7 @@ opt/app/app/Generator/Report/Support.php opt/app/app/Generator/Report/Tag/MonthReportGenerator.php opt/app/app/Generator/Report/Tag/MultiYearReportGenerator.php opt/app/app/Generator/Report/Tag/YearReportGenerator.php +opt/app/app/Handlers/Events/APIEventHandler.php opt/app/app/Handlers/Events/AdminEventHandler.php opt/app/app/Handlers/Events/StoredJournalEventHandler.php opt/app/app/Handlers/Events/UpdatedJournalEventHandler.php @@ -348,6 +351,7 @@ opt/app/app/Helpers/Filter/NegativeAmountFilter.php opt/app/app/Helpers/Filter/OpposingAccountFilter.php opt/app/app/Helpers/Filter/PositiveAmountFilter.php opt/app/app/Helpers/Filter/SplitIndicatorFilter.php +opt/app/app/Helpers/Filter/TransactionViewFilter.php opt/app/app/Helpers/Filter/TransferFilter.php opt/app/app/Helpers/FiscalHelper.php opt/app/app/Helpers/FiscalHelperInterface.php @@ -393,10 +397,10 @@ opt/app/app/Http/Controllers/DebugController.php opt/app/app/Http/Controllers/ExportController.php opt/app/app/Http/Controllers/HelpController.php opt/app/app/Http/Controllers/HomeController.php -opt/app/app/Http/Controllers/Import/ConfigurationController.php opt/app/app/Http/Controllers/Import/IndexController.php +opt/app/app/Http/Controllers/Import/JobConfigurationController.php +opt/app/app/Http/Controllers/Import/JobStatusController.php opt/app/app/Http/Controllers/Import/PrerequisitesController.php -opt/app/app/Http/Controllers/Import/StatusController.php opt/app/app/Http/Controllers/JavascriptController.php opt/app/app/Http/Controllers/Json/AutoCompleteController.php opt/app/app/Http/Controllers/Json/BoxController.php @@ -480,17 +484,17 @@ opt/app/app/Http/Requests/UserFormRequest.php opt/app/app/Http/Requests/UserRegistrationRequest.php opt/app/app/Import/Configuration/BunqConfigurator.php opt/app/app/Import/Configuration/ConfiguratorInterface.php -opt/app/app/Import/Configuration/FileConfigurator.php -opt/app/app/Import/Configuration/SpectreConfigurator.php opt/app/app/Import/Converter/Amount.php opt/app/app/Import/Converter/AmountCredit.php opt/app/app/Import/Converter/AmountDebit.php opt/app/app/Import/Converter/ConverterInterface.php opt/app/app/Import/Converter/INGDebitCredit.php opt/app/app/Import/Converter/RabobankDebitCredit.php -opt/app/app/Import/FileProcessor/CsvProcessor.php -opt/app/app/Import/FileProcessor/FileProcessorInterface.php -opt/app/app/Import/Logging/CommandHandler.php +opt/app/app/Import/JobConfiguration/BunqJobConfiguration.php +opt/app/app/Import/JobConfiguration/FakeJobConfiguration.php +opt/app/app/Import/JobConfiguration/FileJobConfiguration.php +opt/app/app/Import/JobConfiguration/JobConfigurationInterface.php +opt/app/app/Import/JobConfiguration/SpectreJobConfiguration.php opt/app/app/Import/Mapper/AssetAccountIbans.php opt/app/app/Import/Mapper/AssetAccounts.php opt/app/app/Import/Mapper/Bills.php @@ -511,10 +515,12 @@ opt/app/app/Import/Object/ImportCategory.php opt/app/app/Import/Object/ImportCurrency.php opt/app/app/Import/Object/ImportJournal.php opt/app/app/Import/Prerequisites/BunqPrerequisites.php +opt/app/app/Import/Prerequisites/FakePrerequisites.php opt/app/app/Import/Prerequisites/FilePrerequisites.php opt/app/app/Import/Prerequisites/PrerequisitesInterface.php opt/app/app/Import/Prerequisites/SpectrePrerequisites.php opt/app/app/Import/Routine/BunqRoutine.php +opt/app/app/Import/Routine/FakeRoutine.php opt/app/app/Import/Routine/FileRoutine.php opt/app/app/Import/Routine/RoutineInterface.php opt/app/app/Import/Routine/SpectreRoutine.php @@ -524,14 +530,15 @@ opt/app/app/Import/Specifics/PresidentsChoice.php opt/app/app/Import/Specifics/RabobankDescription.php opt/app/app/Import/Specifics/SnsDescription.php opt/app/app/Import/Specifics/SpecificInterface.php -opt/app/app/Import/Storage/ImportStorage.php -opt/app/app/Import/Storage/ImportSupport.php +opt/app/app/Import/Storage/ImportArrayStorage.php opt/app/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php opt/app/app/Jobs/ExecuteRuleOnExistingTransactions.php opt/app/app/Jobs/Job.php opt/app/app/Jobs/MailError.php +opt/app/app/Mail/AccessTokenCreatedMail.php opt/app/app/Mail/AdminTestMail.php opt/app/app/Mail/ConfirmEmailChangeMail.php +opt/app/app/Mail/OAuthTokenCreatedMail.php opt/app/app/Mail/RegisteredUser.php opt/app/app/Mail/RequestedNewPassword.php opt/app/app/Mail/UndoEmailChangeMail.php @@ -627,46 +634,17 @@ opt/app/app/Repositories/User/UserRepositoryInterface.php opt/app/app/Rules/BelongsUser.php opt/app/app/Rules/UniqueIban.php opt/app/app/Rules/ValidTransactions.php -opt/app/app/Services/Bunq/Id/BunqId.php -opt/app/app/Services/Bunq/Id/DeviceServerId.php -opt/app/app/Services/Bunq/Id/DeviceSessionId.php -opt/app/app/Services/Bunq/Id/InstallationId.php -opt/app/app/Services/Bunq/Object/Alias.php -opt/app/app/Services/Bunq/Object/Amount.php -opt/app/app/Services/Bunq/Object/Avatar.php -opt/app/app/Services/Bunq/Object/BunqObject.php -opt/app/app/Services/Bunq/Object/DeviceServer.php -opt/app/app/Services/Bunq/Object/Image.php -opt/app/app/Services/Bunq/Object/LabelMonetaryAccount.php -opt/app/app/Services/Bunq/Object/LabelUser.php -opt/app/app/Services/Bunq/Object/MonetaryAccountBank.php -opt/app/app/Services/Bunq/Object/MonetaryAccountProfile.php -opt/app/app/Services/Bunq/Object/MonetaryAccountSetting.php -opt/app/app/Services/Bunq/Object/NotificationFilter.php -opt/app/app/Services/Bunq/Object/Payment.php -opt/app/app/Services/Bunq/Object/ServerPublicKey.php -opt/app/app/Services/Bunq/Object/UserCompany.php -opt/app/app/Services/Bunq/Object/UserLight.php -opt/app/app/Services/Bunq/Object/UserPerson.php -opt/app/app/Services/Bunq/Request/BunqRequest.php -opt/app/app/Services/Bunq/Request/DeleteDeviceSessionRequest.php -opt/app/app/Services/Bunq/Request/DeviceServerRequest.php -opt/app/app/Services/Bunq/Request/DeviceSessionRequest.php -opt/app/app/Services/Bunq/Request/InstallationTokenRequest.php -opt/app/app/Services/Bunq/Request/ListDeviceServerRequest.php -opt/app/app/Services/Bunq/Request/ListMonetaryAccountRequest.php -opt/app/app/Services/Bunq/Request/ListPaymentRequest.php -opt/app/app/Services/Bunq/Request/ListUserRequest.php -opt/app/app/Services/Bunq/Token/BunqToken.php -opt/app/app/Services/Bunq/Token/InstallationToken.php -opt/app/app/Services/Bunq/Token/SessionToken.php +opt/app/app/Services/Bunq/ApiContext.php +opt/app/app/Services/Bunq/MonetaryAccount.php +opt/app/app/Services/Bunq/Payment.php opt/app/app/Services/Currency/ExchangeRateInterface.php -opt/app/app/Services/Currency/FixerIO.php opt/app/app/Services/Currency/FixerIOv2.php opt/app/app/Services/Github/Object/GithubObject.php opt/app/app/Services/Github/Object/Release.php opt/app/app/Services/Github/Request/GithubRequest.php opt/app/app/Services/Github/Request/UpdateRequest.php +opt/app/app/Services/IP/IPRetrievalInterface.php +opt/app/app/Services/IP/IpifyOrg.php opt/app/app/Services/Internal/Destroy/AccountDestroyService.php opt/app/app/Services/Internal/Destroy/BillDestroyService.php opt/app/app/Services/Internal/Destroy/CategoryDestroyService.php @@ -683,11 +661,11 @@ opt/app/app/Services/Internal/Update/CategoryUpdateService.php opt/app/app/Services/Internal/Update/CurrencyUpdateService.php opt/app/app/Services/Internal/Update/JournalUpdateService.php opt/app/app/Services/Internal/Update/TransactionUpdateService.php -opt/app/app/Services/Password/PwndVerifier.php opt/app/app/Services/Password/PwndVerifierV2.php opt/app/app/Services/Password/Verifier.php opt/app/app/Services/Spectre/Exception/DuplicatedCustomerException.php opt/app/app/Services/Spectre/Exception/SpectreException.php +opt/app/app/Services/Spectre/Exception/WrongRequestFormatException.php opt/app/app/Services/Spectre/Object/Account.php opt/app/app/Services/Spectre/Object/Attempt.php opt/app/app/Services/Spectre/Object/Customer.php @@ -711,13 +689,14 @@ opt/app/app/Support/Binder/BudgetList.php opt/app/app/Support/Binder/CategoryList.php opt/app/app/Support/Binder/CurrencyCode.php opt/app/app/Support/Binder/Date.php +opt/app/app/Support/Binder/ImportProvider.php opt/app/app/Support/Binder/JournalList.php +opt/app/app/Support/Binder/SimpleJournalList.php opt/app/app/Support/Binder/TagList.php opt/app/app/Support/Binder/UnfinishedJournal.php opt/app/app/Support/CacheProperties.php opt/app/app/Support/ChartColour.php opt/app/app/Support/Domain.php -opt/app/app/Support/Events/BillScanner.php opt/app/app/Support/ExpandedForm.php opt/app/app/Support/Facades/Amount.php opt/app/app/Support/Facades/ExpandedForm.php @@ -726,15 +705,44 @@ opt/app/app/Support/Facades/Navigation.php opt/app/app/Support/Facades/Preferences.php opt/app/app/Support/Facades/Steam.php opt/app/app/Support/FireflyConfig.php -opt/app/app/Support/Import/Configuration/Bunq/HaveAccounts.php -opt/app/app/Support/Import/Configuration/ConfigurationInterface.php -opt/app/app/Support/Import/Configuration/File/Initial.php -opt/app/app/Support/Import/Configuration/File/Map.php -opt/app/app/Support/Import/Configuration/File/Roles.php -opt/app/app/Support/Import/Configuration/File/UploadConfig.php -opt/app/app/Support/Import/Configuration/Spectre/HaveAccounts.php opt/app/app/Support/Import/Information/BunqInformation.php +opt/app/app/Support/Import/Information/GetSpectreCustomerTrait.php +opt/app/app/Support/Import/Information/GetSpectreTokenTrait.php opt/app/app/Support/Import/Information/InformationInterface.php +opt/app/app/Support/Import/JobConfiguration/Bunq/BunqJobConfigurationInterface.php +opt/app/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php +opt/app/app/Support/Import/JobConfiguration/Bunq/NewBunqJobHandler.php +opt/app/app/Support/Import/JobConfiguration/File/ConfigureMappingHandler.php +opt/app/app/Support/Import/JobConfiguration/File/ConfigureRolesHandler.php +opt/app/app/Support/Import/JobConfiguration/File/ConfigureUploadHandler.php +opt/app/app/Support/Import/JobConfiguration/File/FileConfigurationInterface.php +opt/app/app/Support/Import/JobConfiguration/File/NewFileJobHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/AuthenticatedHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/ChooseAccountsHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/ChooseLoginHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/DoAuthenticateHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/NewSpectreJobHandler.php +opt/app/app/Support/Import/JobConfiguration/Spectre/SpectreJobConfigurationInterface.php +opt/app/app/Support/Import/Placeholder/ColumnValue.php +opt/app/app/Support/Import/Placeholder/ImportTransaction.php +opt/app/app/Support/Import/Routine/Bunq/StageImportDataHandler.php +opt/app/app/Support/Import/Routine/Bunq/StageNewHandler.php +opt/app/app/Support/Import/Routine/Fake/StageAhoyHandler.php +opt/app/app/Support/Import/Routine/Fake/StageFinalHandler.php +opt/app/app/Support/Import/Routine/Fake/StageNewHandler.php +opt/app/app/Support/Import/Routine/File/AssetAccountMapper.php +opt/app/app/Support/Import/Routine/File/CSVProcessor.php +opt/app/app/Support/Import/Routine/File/CurrencyMapper.php +opt/app/app/Support/Import/Routine/File/FileProcessorInterface.php +opt/app/app/Support/Import/Routine/File/ImportableConverter.php +opt/app/app/Support/Import/Routine/File/ImportableCreator.php +opt/app/app/Support/Import/Routine/File/LineReader.php +opt/app/app/Support/Import/Routine/File/MappedValuesValidator.php +opt/app/app/Support/Import/Routine/File/MappingConverger.php +opt/app/app/Support/Import/Routine/File/OpposingAccountMapper.php +opt/app/app/Support/Import/Routine/Spectre/StageAuthenticatedHandler.php +opt/app/app/Support/Import/Routine/Spectre/StageImportDataHandler.php +opt/app/app/Support/Import/Routine/Spectre/StageNewHandler.php opt/app/app/Support/Models/TransactionJournalTrait.php opt/app/app/Support/Navigation.php opt/app/app/Support/Preferences.php @@ -743,10 +751,12 @@ opt/app/app/Support/Search/Search.php opt/app/app/Support/Search/SearchInterface.php opt/app/app/Support/Steam.php opt/app/app/Support/Twig/AmountFormat.php +opt/app/app/Support/Twig/Extension/Account.php opt/app/app/Support/Twig/Extension/Transaction.php opt/app/app/Support/Twig/Extension/TransactionJournal.php opt/app/app/Support/Twig/General.php opt/app/app/Support/Twig/Journal.php +opt/app/app/Support/Twig/Loader/AccountLoader.php opt/app/app/Support/Twig/Loader/TransactionJournalLoader.php opt/app/app/Support/Twig/Loader/TransactionLoader.php opt/app/app/Support/Twig/PiggyBank.php @@ -760,6 +770,7 @@ opt/app/app/TransactionRules/Actions/AppendNotes.php opt/app/app/TransactionRules/Actions/ClearBudget.php opt/app/app/TransactionRules/Actions/ClearCategory.php opt/app/app/TransactionRules/Actions/ClearNotes.php +opt/app/app/TransactionRules/Actions/LinkToBill.php opt/app/app/TransactionRules/Actions/PrependDescription.php opt/app/app/TransactionRules/Actions/PrependNotes.php opt/app/app/TransactionRules/Actions/RemoveAllTags.php @@ -780,6 +791,7 @@ opt/app/app/TransactionRules/Triggers/AmountLess.php opt/app/app/TransactionRules/Triggers/AmountMore.php opt/app/app/TransactionRules/Triggers/BudgetIs.php opt/app/app/TransactionRules/Triggers/CategoryIs.php +opt/app/app/TransactionRules/Triggers/CurrencyIs.php opt/app/app/TransactionRules/Triggers/DescriptionContains.php opt/app/app/TransactionRules/Triggers/DescriptionEnds.php opt/app/app/TransactionRules/Triggers/DescriptionIs.php @@ -814,6 +826,7 @@ opt/app/app/Transformers/AttachmentTransformer.php opt/app/app/Transformers/BillTransformer.php opt/app/app/Transformers/BudgetTransformer.php opt/app/app/Transformers/CategoryTransformer.php +opt/app/app/Transformers/CurrencyTransformer.php opt/app/app/Transformers/JournalMetaTransformer.php opt/app/app/Transformers/PiggyBankEventTransformer.php opt/app/app/Transformers/PiggyBankTransformer.php @@ -852,7 +865,6 @@ opt/app/config/twigbridge.php opt/app/config/upgrade.php opt/app/config/view.php opt/app/database/factories/ModelFactory.php -opt/app/database/migrations opt/app/database/migrations/2016_06_16_000000_create_support_tables.php opt/app/database/migrations/2016_06_16_000001_create_users_table.php opt/app/database/migrations/2016_06_16_000002_create_main_tables.php @@ -873,6 +885,8 @@ opt/app/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table. opt/app/database/migrations/2018_01_01_000004_create_oauth_clients_table.php opt/app/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php opt/app/database/migrations/2018_03_19_141348_changes_for_v472.php +opt/app/database/migrations/2018_04_07_210913_changes_for_v473.php +opt/app/database/migrations/2018_04_29_174524_changes_for_v474.php opt/app/database/seeds/AccountTypeSeeder.php opt/app/database/seeds/ConfigSeeder.php opt/app/database/seeds/DatabaseSeeder.php @@ -884,6 +898,7 @@ opt/app/docker-compose.yml opt/app/index.php opt/app/package-lock.json opt/app/public/.htaccess +opt/app/public/.well-known/security.txt opt/app/public/android-chrome-192x192.png opt/app/public/android-chrome-512x512.png opt/app/public/apple-touch-icon.png @@ -1038,9 +1053,12 @@ opt/app/public/images/loading-small.gif opt/app/public/images/loading-wide.gif opt/app/public/images/logos/bunq.png opt/app/public/images/logos/csv.png +opt/app/public/images/logos/fake.png opt/app/public/images/logos/file.png opt/app/public/images/logos/plaid.png +opt/app/public/images/logos/quovo.png opt/app/public/images/logos/spectre.png +opt/app/public/images/logos/yodlee.png opt/app/public/images/page_green.png opt/app/public/images/page_white_acrobat.png opt/app/public/index.php @@ -1065,10 +1083,23 @@ opt/app/public/js/ff/export/index.js opt/app/public/js/ff/firefly.js opt/app/public/js/ff/guest.js opt/app/public/js/ff/help.js +opt/app/public/js/ff/import/file/configure-upload.js opt/app/public/js/ff/import/status.js +opt/app/public/js/ff/import/status_v2.js opt/app/public/js/ff/index.js opt/app/public/js/ff/install/index.js opt/app/public/js/ff/intro/intro.js +opt/app/public/js/ff/moment/de_DE.js +opt/app/public/js/ff/moment/en_US.js +opt/app/public/js/ff/moment/es_ES.js +opt/app/public/js/ff/moment/fr_FR.js +opt/app/public/js/ff/moment/id_ID.js +opt/app/public/js/ff/moment/it_IT.js +opt/app/public/js/ff/moment/nl_NL.js +opt/app/public/js/ff/moment/pl_PL.js +opt/app/public/js/ff/moment/pt_BR.js +opt/app/public/js/ff/moment/ru_RU.js +opt/app/public/js/ff/moment/tr_TR.js opt/app/public/js/ff/piggy-banks/create.js opt/app/public/js/ff/piggy-banks/edit.js opt/app/public/js/ff/piggy-banks/index.js @@ -1349,6 +1380,7 @@ opt/app/resources/views/admin/users/index.twig opt/app/resources/views/admin/users/show.twig opt/app/resources/views/attachments/delete.twig opt/app/resources/views/attachments/edit.twig +opt/app/resources/views/attachments/index.twig opt/app/resources/views/auth/login.twig opt/app/resources/views/auth/lost-two-factor.twig opt/app/resources/views/auth/passwords/email.twig @@ -1389,6 +1421,8 @@ opt/app/resources/views/demo/no-demo-text.twig opt/app/resources/views/demo/piggy-banks/index.twig opt/app/resources/views/demo/reports/index.twig opt/app/resources/views/demo/transactions/index.twig +opt/app/resources/views/emails/access-token-created-html.twig +opt/app/resources/views/emails/access-token-created-text.twig opt/app/resources/views/emails/admin-test-html.twig opt/app/resources/views/emails/admin-test-text.twig opt/app/resources/views/emails/confirm-account-html.twig @@ -1401,6 +1435,8 @@ opt/app/resources/views/emails/footer-html.twig opt/app/resources/views/emails/footer-text.twig opt/app/resources/views/emails/header-html.twig opt/app/resources/views/emails/header-text.twig +opt/app/resources/views/emails/oauth-client-created-html.twig +opt/app/resources/views/emails/oauth-client-created-text.twig opt/app/resources/views/emails/password-html.twig opt/app/resources/views/emails/password-text.twig opt/app/resources/views/emails/registered-html.twig @@ -1413,8 +1449,10 @@ opt/app/resources/views/errors/500.twig opt/app/resources/views/errors/503.twig opt/app/resources/views/errors/FireflyException.twig opt/app/resources/views/export/index.twig +opt/app/resources/views/form/amount-no-currency.twig opt/app/resources/views/form/amount-small.twig opt/app/resources/views/form/amount.twig +opt/app/resources/views/form/assetAccountCheckList.twig opt/app/resources/views/form/balance.twig opt/app/resources/views/form/checkbox.twig opt/app/resources/views/form/date.twig @@ -1423,7 +1461,6 @@ opt/app/resources/views/form/file.twig opt/app/resources/views/form/help.twig opt/app/resources/views/form/integer.twig opt/app/resources/views/form/location.twig -opt/app/resources/views/form/multiCheckbox.twig opt/app/resources/views/form/multiRadio.twig opt/app/resources/views/form/non-selectable-amount.twig opt/app/resources/views/form/number.twig @@ -1435,14 +1472,20 @@ opt/app/resources/views/form/tags.twig opt/app/resources/views/form/text.twig opt/app/resources/views/form/textarea.twig opt/app/resources/views/import/bank/form.twig -opt/app/resources/views/import/bunq/accounts.twig +opt/app/resources/views/import/bunq/choose-accounts.twig opt/app/resources/views/import/bunq/prerequisites.twig -opt/app/resources/views/import/file/initial.twig +opt/app/resources/views/import/fake/apply-rules.twig +opt/app/resources/views/import/fake/enter-album.twig +opt/app/resources/views/import/fake/enter-artist.twig +opt/app/resources/views/import/fake/enter-song.twig +opt/app/resources/views/import/fake/prerequisites.twig +opt/app/resources/views/import/file/configure-upload.twig opt/app/resources/views/import/file/map.twig +opt/app/resources/views/import/file/new.twig opt/app/resources/views/import/file/roles.twig -opt/app/resources/views/import/file/upload-config.twig opt/app/resources/views/import/index.twig opt/app/resources/views/import/spectre/accounts.twig +opt/app/resources/views/import/spectre/choose-login.twig opt/app/resources/views/import/spectre/prerequisites.twig opt/app/resources/views/import/spectre/redirect.twig opt/app/resources/views/import/status.twig @@ -1561,7 +1604,6 @@ opt/app/routes/breadcrumbs.php opt/app/routes/channels.php opt/app/routes/console.php opt/app/routes/web.php -opt/app/security.txt opt/app/server.php opt/app/storage opt/app/vendor/autoload.php @@ -1625,9 +1667,339 @@ opt/app/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Encoder/MatrixUtilTest.php opt/app/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/HtmlTest.php opt/app/vendor/bacon/bacon-qr-code/tests/BaconQrCode/Renderer/Text/TextTest.php opt/app/vendor/bacon/bacon-qr-code/tests/bootstrap.php +opt/app/vendor/bin/bunq-install opt/app/vendor/bin/commonmark opt/app/vendor/bin/doctrine-dbal opt/app/vendor/bin/generate-defuse-key +opt/app/vendor/bin/var-dump-server +opt/app/vendor/bunq/sdk_php/.github/ISSUE_TEMPLATE.md +opt/app/vendor/bunq/sdk_php/.github/PULL_REQUEST_TEMPLATE.md +opt/app/vendor/bunq/sdk_php/.gitmodules +opt/app/vendor/bunq/sdk_php/.zappr.yaml +opt/app/vendor/bunq/sdk_php/LICENSE.md +opt/app/vendor/bunq/sdk_php/bin/bunq-install +opt/app/vendor/bunq/sdk_php/composer.json +opt/app/vendor/bunq/sdk_php/src/Context/ApiContext.php +opt/app/vendor/bunq/sdk_php/src/Context/BunqContext.php +opt/app/vendor/bunq/sdk_php/src/Context/InstallationContext.php +opt/app/vendor/bunq/sdk_php/src/Context/SessionContext.php +opt/app/vendor/bunq/sdk_php/src/Context/UserContext.php +opt/app/vendor/bunq/sdk_php/src/Exception/ApiException.php +opt/app/vendor/bunq/sdk_php/src/Exception/BadRequestException.php +opt/app/vendor/bunq/sdk_php/src/Exception/BunqException.php +opt/app/vendor/bunq/sdk_php/src/Exception/EXCEPTION.md +opt/app/vendor/bunq/sdk_php/src/Exception/ExceptionFactory.php +opt/app/vendor/bunq/sdk_php/src/Exception/ForbiddenException.php +opt/app/vendor/bunq/sdk_php/src/Exception/MethodNotAllowedException.php +opt/app/vendor/bunq/sdk_php/src/Exception/NotFoundException.php +opt/app/vendor/bunq/sdk_php/src/Exception/PleaseContactBunqException.php +opt/app/vendor/bunq/sdk_php/src/Exception/SecurityException.php +opt/app/vendor/bunq/sdk_php/src/Exception/TooManyRequestsException.php +opt/app/vendor/bunq/sdk_php/src/Exception/UnauthorizedException.php +opt/app/vendor/bunq/sdk_php/src/Exception/UnknownApiErrorException.php +opt/app/vendor/bunq/sdk_php/src/Http/ApiClient.php +opt/app/vendor/bunq/sdk_php/src/Http/BunqResponse.php +opt/app/vendor/bunq/sdk_php/src/Http/BunqResponseRaw.php +opt/app/vendor/bunq/sdk_php/src/Http/Certificate/api.bunq.com.pubkey.pem +opt/app/vendor/bunq/sdk_php/src/Http/Certificate/sandbox.public.api.bunq.com.pubkey.pem +opt/app/vendor/bunq/sdk_php/src/Http/Handler/HandlerBase.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/HandlerUtil.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/RequestHandlerAuthentication.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/RequestHandlerBase.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/RequestHandlerEncryption.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/RequestHandlerSignature.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/ResponseHandlerBase.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/ResponseHandlerError.php +opt/app/vendor/bunq/sdk_php/src/Http/Handler/ResponseHandlerSignature.php +opt/app/vendor/bunq/sdk_php/src/Http/Pagination.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/AnchorObjectInterface.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/BunqModel.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/BunqResponseInstallation.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/BunqResponseSessionServer.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/DeviceServerInternal.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/Id.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/Installation.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/SandboxUserInternal.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/ServerPublicKey.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/SessionServer.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/Token.php +opt/app/vendor/bunq/sdk_php/src/Model/Core/Uuid.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentConversationContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentMonetaryAccount.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentPublic.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentPublicContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/AttachmentTabContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Avatar.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BillingContractSubscription.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeFundraiserProfile.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeFundraiserResult.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeTabEntry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeTabResultInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqMeTabResultResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseAttachmentPublic.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseAttachmentTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseAvatar.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseBillingContractSubscriptionList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseBunqMeTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseBunqMeTabList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCard.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardDebit.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardGeneratedCvc2.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardGeneratedCvc2List.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardNameList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardPinChange.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardPinChangeList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardResult.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCardResultList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCashRegister.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCashRegisterList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCashRegisterQrCode.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCashRegisterQrCodeList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCertificatePinned.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCertificatePinnedList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseChatConversation.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseChatConversationList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseChatMessageList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCustomer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCustomerLimitList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCustomerList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCustomerStatementExport.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseCustomerStatementExportList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDevice.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDeviceList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDeviceServer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDeviceServerList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftPayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftPaymentList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftShareInviteApiKey.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftShareInviteApiKeyList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftShareInviteBank.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseDraftShareInviteBankList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseExportAnnualOverview.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseExportAnnualOverviewList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseIdealMerchantTransaction.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseIdealMerchantTransactionList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInstallationServerPublicKeyList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInt.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInvoice.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInvoiceByUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInvoiceByUserList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseInvoiceList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMasterCardAction.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMasterCardActionList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccount.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountBank.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountBankList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountJoint.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountJointList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountLight.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountLightList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseMonetaryAccountList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseNull.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePaymentBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePaymentBatchList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePaymentChatList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePaymentList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePermittedIp.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePermittedIpList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponsePromotionDisplay.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestInquiryBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestInquiryBatchList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestInquiryChatList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestInquiryList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestResponseChatList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseRequestResponseList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseSandboxUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseSchedule.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseScheduleInstance.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseScheduleInstanceList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseScheduleList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseSchedulePayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseSchedulePaymentList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseScheduleUserList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseShareInviteBankInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseShareInviteBankInquiryList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseShareInviteBankResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseShareInviteBankResponseList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseString.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabAttachmentTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabItemShop.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabItemShopList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabResultInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabResultInquiryList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabResultResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabResultResponseList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabUsageMultiple.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabUsageMultipleList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabUsageSingle.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTabUsageSingleList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTokenQrRequestIdeal.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseTokenQrRequestSofort.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserCompany.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserCredentialPasswordIp.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserCredentialPasswordIpList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserLight.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserList.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/BunqResponseUserPerson.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Card.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardDebit.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardGeneratedCvc2.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardName.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardPinChange.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardReplace.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CardResult.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CashRegister.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CashRegisterQrCode.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CashRegisterQrCodeContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CertificatePinned.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatConversation.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatConversationReference.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatConversationSupportExternal.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessage.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessageAnnouncement.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessageAttachment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessageStatus.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessageText.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ChatMessageUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Customer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CustomerLimit.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CustomerStatementExport.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/CustomerStatementExportContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Device.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DeviceServer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DraftPayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DraftShareInviteApiKey.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DraftShareInviteApiKeyQrCodeContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DraftShareInviteBank.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/DraftShareInviteBankQrCodeContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ExportAnnualOverview.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ExportAnnualOverviewContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/IdealMerchantTransaction.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/InstallationServerPublicKey.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Invoice.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/InvoiceByUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MasterCardAction.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MonetaryAccount.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MonetaryAccountBank.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MonetaryAccountJoint.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MonetaryAccountLight.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/MonetaryAccountProfile.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Payment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/PaymentBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/PaymentChat.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/PermittedIp.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/PromotionDisplay.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/RequestInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/RequestInquiryBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/RequestInquiryChat.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/RequestResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/RequestResponseChat.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/SandboxUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Schedule.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ScheduleInstance.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/SchedulePayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/SchedulePaymentBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ScheduleUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Session.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ShareInviteBankAmountUsed.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ShareInviteBankInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/ShareInviteBankResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Tab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabAttachmentTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabAttachmentTabContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabItem.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabItemShop.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabItemShopBatch.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabQrCodeContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabResultInquiry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabResultResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabUsageMultiple.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TabUsageSingle.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TokenQrRequestIdeal.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/TokenQrRequestSofort.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/User.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/UserCompany.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/UserCredentialPasswordIp.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/UserLight.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/UserPerson.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/Whitelist.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Endpoint/WhitelistResult.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Address.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Amount.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/AnchoredObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Attachment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/AttachmentMonetaryAccountPayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/AttachmentPublic.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/AttachmentTab.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Avatar.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/BudgetRestriction.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/BunqId.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/BunqMeMerchantAvailable.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/CardCountryPermission.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/CardLimit.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/CardMagStripePermission.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/CardPinAssignment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Certificate.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentAnchorEvent.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentAttachment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentGeolocation.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentStatusConversation.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentStatusConversationTitle.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentStatusMembership.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ChatMessageContentText.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/CoOwner.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/DraftPaymentAnchorObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/DraftPaymentEntry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/DraftPaymentResponse.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/DraftShareInviteEntry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Error.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Geolocation.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Image.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/InvoiceItem.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/InvoiceItemGroup.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Issuer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/LabelCard.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/LabelMonetaryAccount.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/LabelUser.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/MonetaryAccountProfileDrain.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/MonetaryAccountProfileFill.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/MonetaryAccountSetting.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/NotificationAnchorObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/NotificationFilter.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/NotificationUrl.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/PermittedDevice.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Pointer.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/RequestInquiryReference.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/RequestReferenceSplitTheBillAnchorObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ScheduleAnchorObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ScheduleInstanceAnchorObject.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/SchedulePaymentEntry.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ShareDetail.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ShareDetailDraftPayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ShareDetailPayment.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/ShareDetailReadOnly.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/TabTextWaitingScreen.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/TabVisibility.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/TaxResident.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/Ubo.php +opt/app/vendor/bunq/sdk_php/src/Model/Generated/Object/WhitelistResultViewAnchoredObject.php +opt/app/vendor/bunq/sdk_php/src/Security/KeyPair.php +opt/app/vendor/bunq/sdk_php/src/Security/PrivateKey.php +opt/app/vendor/bunq/sdk_php/src/Security/PublicKey.php +opt/app/vendor/bunq/sdk_php/src/Util/BunqEnum.php +opt/app/vendor/bunq/sdk_php/src/Util/BunqEnumApiEnvironmentType.php +opt/app/vendor/bunq/sdk_php/src/Util/FileUtil.php +opt/app/vendor/bunq/sdk_php/src/Util/InstallationUtil.php +opt/app/vendor/bunq/sdk_php/src/Util/ModelUtil.php opt/app/vendor/composer/ClassLoader.php opt/app/vendor/composer/LICENSE opt/app/vendor/composer/autoload_classmap.php @@ -2179,7 +2551,6 @@ opt/app/vendor/egulias/email-validator/EmailValidator/Warning/Warning.php opt/app/vendor/egulias/email-validator/LICENSE opt/app/vendor/egulias/email-validator/README.md opt/app/vendor/egulias/email-validator/composer.json -opt/app/vendor/egulias/email-validator/composer.lock opt/app/vendor/egulias/email-validator/phpunit.xml.dist opt/app/vendor/erusev/parsedown/LICENSE.txt opt/app/vendor/erusev/parsedown/Parsedown.php @@ -2476,6 +2847,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Queue.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableCollection.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableEntity.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/ShouldQueue.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Redis/Connection.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Redis/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Redis/LimiterTimeoutException.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/BindingRegistrar.php @@ -2682,6 +3054,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ListenerMakeC opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/MailMakeCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ModelMakeCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/NotificationMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ObserverMakeCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/PackageDiscoverCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/PolicyMakeCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/PresetCommand.php @@ -2733,6 +3106,8 @@ opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/markdow opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/markdown.stub opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/model.stub opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/notification.stub +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/observer.plain.stub +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/observer.stub opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/pivot.model.stub opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/policy.plain.stub opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/stubs/policy.stub @@ -4141,109 +4516,6 @@ opt/app/vendor/rcrowe/twigbridge/src/ServiceProvider.php opt/app/vendor/rcrowe/twigbridge/src/Twig/Globals.php opt/app/vendor/rcrowe/twigbridge/src/Twig/Loader.php opt/app/vendor/rcrowe/twigbridge/src/Twig/Template.php -opt/app/vendor/rmccue/requests/.coveralls.yml -opt/app/vendor/rmccue/requests/CHANGELOG.md -opt/app/vendor/rmccue/requests/LICENSE -opt/app/vendor/rmccue/requests/README.md -opt/app/vendor/rmccue/requests/bin/create_pear_package.php -opt/app/vendor/rmccue/requests/composer.json -opt/app/vendor/rmccue/requests/docs/README.md -opt/app/vendor/rmccue/requests/docs/authentication-custom.md -opt/app/vendor/rmccue/requests/docs/authentication.md -opt/app/vendor/rmccue/requests/docs/goals.md -opt/app/vendor/rmccue/requests/docs/hooks.md -opt/app/vendor/rmccue/requests/docs/proxy.md -opt/app/vendor/rmccue/requests/docs/usage-advanced.md -opt/app/vendor/rmccue/requests/docs/usage.md -opt/app/vendor/rmccue/requests/docs/why-requests.md -opt/app/vendor/rmccue/requests/examples/basic-auth.php -opt/app/vendor/rmccue/requests/examples/cookie.php -opt/app/vendor/rmccue/requests/examples/cookie_jar.php -opt/app/vendor/rmccue/requests/examples/get.php -opt/app/vendor/rmccue/requests/examples/multiple.php -opt/app/vendor/rmccue/requests/examples/post.php -opt/app/vendor/rmccue/requests/examples/proxy.php -opt/app/vendor/rmccue/requests/examples/session.php -opt/app/vendor/rmccue/requests/examples/timeout.php -opt/app/vendor/rmccue/requests/library/Requests.php -opt/app/vendor/rmccue/requests/library/Requests/Auth.php -opt/app/vendor/rmccue/requests/library/Requests/Auth/Basic.php -opt/app/vendor/rmccue/requests/library/Requests/Cookie.php -opt/app/vendor/rmccue/requests/library/Requests/Cookie/Jar.php -opt/app/vendor/rmccue/requests/library/Requests/Exception.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/304.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/305.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/306.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/400.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/401.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/402.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/403.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/404.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/405.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/406.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/407.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/408.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/409.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/410.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/411.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/412.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/413.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/414.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/415.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/416.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/417.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/418.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/428.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/429.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/431.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/500.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/501.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/502.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/503.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/504.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/505.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/511.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/HTTP/Unknown.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/Transport.php -opt/app/vendor/rmccue/requests/library/Requests/Exception/Transport/cURL.php -opt/app/vendor/rmccue/requests/library/Requests/Hooker.php -opt/app/vendor/rmccue/requests/library/Requests/Hooks.php -opt/app/vendor/rmccue/requests/library/Requests/IDNAEncoder.php -opt/app/vendor/rmccue/requests/library/Requests/IPv6.php -opt/app/vendor/rmccue/requests/library/Requests/IRI.php -opt/app/vendor/rmccue/requests/library/Requests/Proxy.php -opt/app/vendor/rmccue/requests/library/Requests/Proxy/HTTP.php -opt/app/vendor/rmccue/requests/library/Requests/Response.php -opt/app/vendor/rmccue/requests/library/Requests/Response/Headers.php -opt/app/vendor/rmccue/requests/library/Requests/SSL.php -opt/app/vendor/rmccue/requests/library/Requests/Session.php -opt/app/vendor/rmccue/requests/library/Requests/Transport.php -opt/app/vendor/rmccue/requests/library/Requests/Transport/cURL.php -opt/app/vendor/rmccue/requests/library/Requests/Transport/cacert.pem -opt/app/vendor/rmccue/requests/library/Requests/Transport/fsockopen.php -opt/app/vendor/rmccue/requests/library/Requests/Utility/CaseInsensitiveDictionary.php -opt/app/vendor/rmccue/requests/library/Requests/Utility/FilteredIterator.php -opt/app/vendor/rmccue/requests/package.xml.tpl -opt/app/vendor/rmccue/requests/tests/Auth/Basic.php -opt/app/vendor/rmccue/requests/tests/ChunkedEncoding.php -opt/app/vendor/rmccue/requests/tests/Cookies.php -opt/app/vendor/rmccue/requests/tests/Encoding.php -opt/app/vendor/rmccue/requests/tests/IDNAEncoder.php -opt/app/vendor/rmccue/requests/tests/IRI.php -opt/app/vendor/rmccue/requests/tests/Proxy/HTTP.php -opt/app/vendor/rmccue/requests/tests/Requests.php -opt/app/vendor/rmccue/requests/tests/Response/Headers.php -opt/app/vendor/rmccue/requests/tests/SSL.php -opt/app/vendor/rmccue/requests/tests/Session.php -opt/app/vendor/rmccue/requests/tests/Transport/Base.php -opt/app/vendor/rmccue/requests/tests/Transport/cURL.php -opt/app/vendor/rmccue/requests/tests/Transport/fsockopen.php -opt/app/vendor/rmccue/requests/tests/bootstrap.php -opt/app/vendor/rmccue/requests/tests/phpunit.xml.dist -opt/app/vendor/rmccue/requests/tests/utils/proxy/proxy.py -opt/app/vendor/rmccue/requests/tests/utils/proxy/start.sh -opt/app/vendor/rmccue/requests/tests/utils/proxy/stop.sh opt/app/vendor/swiftmailer/swiftmailer/.gitattributes opt/app/vendor/swiftmailer/swiftmailer/.github/ISSUE_TEMPLATE.md opt/app/vendor/swiftmailer/swiftmailer/.github/PULL_REQUEST_TEMPLATE.md @@ -4595,6 +4867,7 @@ opt/app/vendor/symfony/console/Exception/ExceptionInterface.php opt/app/vendor/symfony/console/Exception/InvalidArgumentException.php opt/app/vendor/symfony/console/Exception/InvalidOptionException.php opt/app/vendor/symfony/console/Exception/LogicException.php +opt/app/vendor/symfony/console/Exception/NamespaceNotFoundException.php opt/app/vendor/symfony/console/Exception/RuntimeException.php opt/app/vendor/symfony/console/Formatter/OutputFormatter.php opt/app/vendor/symfony/console/Formatter/OutputFormatterInterface.php @@ -4615,6 +4888,7 @@ opt/app/vendor/symfony/console/Helper/QuestionHelper.php opt/app/vendor/symfony/console/Helper/SymfonyQuestionHelper.php opt/app/vendor/symfony/console/Helper/Table.php opt/app/vendor/symfony/console/Helper/TableCell.php +opt/app/vendor/symfony/console/Helper/TableRows.php opt/app/vendor/symfony/console/Helper/TableSeparator.php opt/app/vendor/symfony/console/Helper/TableStyle.php opt/app/vendor/symfony/console/Input/ArgvInput.php @@ -4632,6 +4906,7 @@ opt/app/vendor/symfony/console/Logger/ConsoleLogger.php opt/app/vendor/symfony/console/Output/BufferedOutput.php opt/app/vendor/symfony/console/Output/ConsoleOutput.php opt/app/vendor/symfony/console/Output/ConsoleOutputInterface.php +opt/app/vendor/symfony/console/Output/ConsoleSectionOutput.php opt/app/vendor/symfony/console/Output/NullOutput.php opt/app/vendor/symfony/console/Output/Output.php opt/app/vendor/symfony/console/Output/OutputInterface.php @@ -4647,6 +4922,7 @@ opt/app/vendor/symfony/console/Style/SymfonyStyle.php opt/app/vendor/symfony/console/Terminal.php opt/app/vendor/symfony/console/Tester/ApplicationTester.php opt/app/vendor/symfony/console/Tester/CommandTester.php +opt/app/vendor/symfony/console/Tester/TesterTrait.php opt/app/vendor/symfony/console/Tests/ApplicationTest.php opt/app/vendor/symfony/console/Tests/Command/CommandTest.php opt/app/vendor/symfony/console/Tests/Command/HelpCommandTest.php @@ -4686,6 +4962,7 @@ opt/app/vendor/symfony/console/Tests/Fixtures/FooSameCaseLowercaseCommand.php opt/app/vendor/symfony/console/Tests/Fixtures/FooSameCaseUppercaseCommand.php opt/app/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced1Command.php opt/app/vendor/symfony/console/Tests/Fixtures/FooSubnamespaced2Command.php +opt/app/vendor/symfony/console/Tests/Fixtures/FooWithoutAliasCommand.php opt/app/vendor/symfony/console/Tests/Fixtures/FoobarCommand.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_0.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_1.php @@ -4700,6 +4977,7 @@ opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_2.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_3.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4.php +opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_4_with_iterators.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_5.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_6.php opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/command/command_7.php @@ -4720,6 +4998,7 @@ opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_1 opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_2.txt opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_3.txt opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4.txt +opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_4_with_iterators.txt opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_5.txt opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_6.txt opt/app/vendor/symfony/console/Tests/Fixtures/Style/SymfonyStyle/output/output_7.txt @@ -4863,6 +5142,7 @@ opt/app/vendor/symfony/console/Tests/Input/InputTest.php opt/app/vendor/symfony/console/Tests/Input/StringInputTest.php opt/app/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php opt/app/vendor/symfony/console/Tests/Output/ConsoleOutputTest.php +opt/app/vendor/symfony/console/Tests/Output/ConsoleSectionOutputTest.php opt/app/vendor/symfony/console/Tests/Output/NullOutputTest.php opt/app/vendor/symfony/console/Tests/Output/OutputTest.php opt/app/vendor/symfony/console/Tests/Output/StreamOutputTest.php @@ -5107,8 +5387,15 @@ opt/app/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php opt/app/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php opt/app/vendor/symfony/http-foundation/ExpressionRequestMatcher.php opt/app/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php +opt/app/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php +opt/app/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php opt/app/vendor/symfony/http-foundation/File/Exception/FileException.php opt/app/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php +opt/app/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php +opt/app/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php +opt/app/vendor/symfony/http-foundation/File/Exception/NoFileException.php +opt/app/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php +opt/app/vendor/symfony/http-foundation/File/Exception/PartialFileException.php opt/app/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php opt/app/vendor/symfony/http-foundation/File/Exception/UploadException.php opt/app/vendor/symfony/http-foundation/File/File.php @@ -5123,6 +5410,7 @@ opt/app/vendor/symfony/http-foundation/File/Stream.php opt/app/vendor/symfony/http-foundation/File/UploadedFile.php opt/app/vendor/symfony/http-foundation/FileBag.php opt/app/vendor/symfony/http-foundation/HeaderBag.php +opt/app/vendor/symfony/http-foundation/HeaderUtils.php opt/app/vendor/symfony/http-foundation/IpUtils.php opt/app/vendor/symfony/http-foundation/JsonResponse.php opt/app/vendor/symfony/http-foundation/LICENSE @@ -5148,10 +5436,12 @@ opt/app/vendor/symfony/http-foundation/Session/SessionBagProxy.php opt/app/vendor/symfony/http-foundation/Session/SessionInterface.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php +opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php +opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php opt/app/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php opt/app/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php @@ -5178,7 +5468,21 @@ opt/app/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif opt/app/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php opt/app/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php opt/app/vendor/symfony/http-foundation/Tests/FileBagTest.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected +opt/app/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php opt/app/vendor/symfony/http-foundation/Tests/HeaderBagTest.php +opt/app/vendor/symfony/http-foundation/Tests/HeaderUtilsTest.php opt/app/vendor/symfony/http-foundation/Tests/IpUtilsTest.php opt/app/vendor/symfony/http-foundation/Tests/JsonResponseTest.php opt/app/vendor/symfony/http-foundation/Tests/ParameterBagTest.php @@ -5186,6 +5490,7 @@ opt/app/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php opt/app/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php opt/app/vendor/symfony/http-foundation/Tests/RequestStackTest.php opt/app/vendor/symfony/http-foundation/Tests/RequestTest.php +opt/app/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php opt/app/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php opt/app/vendor/symfony/http-foundation/Tests/ResponseTest.php opt/app/vendor/symfony/http-foundation/Tests/ResponseTestCase.php @@ -5195,6 +5500,7 @@ opt/app/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttribu opt/app/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/SessionTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected @@ -5210,10 +5516,15 @@ opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/wi opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MigratingSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php +opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php @@ -5245,6 +5556,7 @@ opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeV opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php +opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/TraceableValueResolver.php opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php opt/app/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php opt/app/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php @@ -5369,6 +5681,7 @@ opt/app/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php opt/app/vendor/symfony/http-kernel/Tests/ClientTest.php opt/app/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php opt/app/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php +opt/app/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/TraceableValueResolverTest.php opt/app/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php opt/app/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php opt/app/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php @@ -5488,6 +5801,11 @@ opt/app/vendor/symfony/http-kernel/Tests/UriSignerTest.php opt/app/vendor/symfony/http-kernel/UriSigner.php opt/app/vendor/symfony/http-kernel/composer.json opt/app/vendor/symfony/http-kernel/phpunit.xml.dist +opt/app/vendor/symfony/polyfill-ctype/Ctype.php +opt/app/vendor/symfony/polyfill-ctype/LICENSE +opt/app/vendor/symfony/polyfill-ctype/README.md +opt/app/vendor/symfony/polyfill-ctype/bootstrap.php +opt/app/vendor/symfony/polyfill-ctype/composer.json opt/app/vendor/symfony/polyfill-mbstring/LICENSE opt/app/vendor/symfony/polyfill-mbstring/Mbstring.php opt/app/vendor/symfony/polyfill-mbstring/README.md @@ -5519,6 +5837,7 @@ opt/app/vendor/symfony/process/Exception/ExceptionInterface.php opt/app/vendor/symfony/process/Exception/InvalidArgumentException.php opt/app/vendor/symfony/process/Exception/LogicException.php opt/app/vendor/symfony/process/Exception/ProcessFailedException.php +opt/app/vendor/symfony/process/Exception/ProcessSignaledException.php opt/app/vendor/symfony/process/Exception/ProcessTimedOutException.php opt/app/vendor/symfony/process/Exception/RuntimeException.php opt/app/vendor/symfony/process/ExecutableFinder.php @@ -5596,8 +5915,6 @@ opt/app/vendor/symfony/routing/Loader/PhpFileLoader.php opt/app/vendor/symfony/routing/Loader/XmlFileLoader.php opt/app/vendor/symfony/routing/Loader/YamlFileLoader.php opt/app/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd -opt/app/vendor/symfony/routing/Matcher/Dumper/DumperCollection.php -opt/app/vendor/symfony/routing/Matcher/Dumper/DumperRoute.php opt/app/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php opt/app/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php opt/app/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php @@ -5626,6 +5943,24 @@ opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/AbstractClassController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/ActionPathController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/ExplicitLocalizedActionPathController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/InvokableController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/InvokableLocalizedController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedActionPathController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedMethodActionControllers.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixLocalizedActionController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixMissingLocaleActionController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixMissingRouteLocaleActionController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixWithRouteWithoutLocale.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/MethodActionControllers.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/MissingRouteNameController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/NothingButNameController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/PrefixedActionLocalizedRouteController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/PrefixedActionPathController.php +opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/RouteWithPrefixController.php opt/app/vendor/symfony/routing/Tests/Fixtures/CustomCompiledRoute.php opt/app/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php opt/app/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php @@ -5652,12 +5987,18 @@ opt/app/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml opt/app/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher10.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher11.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher12.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher13.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher8.php +opt/app/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher9.php opt/app/vendor/symfony/routing/Tests/Fixtures/empty.yml opt/app/vendor/symfony/routing/Tests/Fixtures/file_resource.yml opt/app/vendor/symfony/routing/Tests/Fixtures/foo.xml @@ -5673,11 +6014,31 @@ opt/app/vendor/symfony/routing/Tests/Fixtures/glob/import_single.yml opt/app/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl.php opt/app/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_bar.php opt/app/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_baz.php +opt/app/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.yml opt/app/vendor/symfony/routing/Tests/Fixtures/incomplete.yml opt/app/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-controller-default.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.xml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/importing-localized-route.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/localized-route.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/missing-locale-in-importer.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/not-localized.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/officially_formatted_locales.yml +opt/app/vendor/symfony/routing/Tests/Fixtures/localized/route-without-path-or-locales.yml opt/app/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml @@ -5695,7 +6056,11 @@ opt/app/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml opt/app/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml opt/app/vendor/symfony/routing/Tests/Fixtures/null_values.xml opt/app/vendor/symfony/routing/Tests/Fixtures/php_dsl.php +opt/app/vendor/symfony/routing/Tests/Fixtures/php_dsl_i18n.php opt/app/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php +opt/app/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_i18n.php +opt/app/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_root.php +opt/app/vendor/symfony/routing/Tests/Fixtures/php_object_dsl.php opt/app/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml opt/app/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml opt/app/vendor/symfony/routing/Tests/Fixtures/validpattern.php @@ -5714,6 +6079,7 @@ opt/app/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php +opt/app/vendor/symfony/routing/Tests/Loader/FileLocatorStub.php opt/app/vendor/symfony/routing/Tests/Loader/GlobFileLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php @@ -5721,7 +6087,6 @@ opt/app/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php opt/app/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php opt/app/vendor/symfony/routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php opt/app/vendor/symfony/routing/Tests/Matcher/DumpedUrlMatcherTest.php -opt/app/vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php opt/app/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php opt/app/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php opt/app/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -5805,6 +6170,7 @@ opt/app/vendor/symfony/translation/Resources/schemas/xliff-core-1.2-strict.xsd opt/app/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php opt/app/vendor/symfony/translation/Tests/Catalogue/MergeOperationTest.php opt/app/vendor/symfony/translation/Tests/Catalogue/TargetOperationTest.php +opt/app/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php opt/app/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php opt/app/vendor/symfony/translation/Tests/DataCollectorTranslatorTest.php opt/app/vendor/symfony/translation/Tests/DependencyInjection/TranslationDumperPassTest.php @@ -5921,6 +6287,7 @@ opt/app/vendor/symfony/var-dumper/Caster/DoctrineCaster.php opt/app/vendor/symfony/var-dumper/Caster/EnumStub.php opt/app/vendor/symfony/var-dumper/Caster/ExceptionCaster.php opt/app/vendor/symfony/var-dumper/Caster/FrameStub.php +opt/app/vendor/symfony/var-dumper/Caster/GmpCaster.php opt/app/vendor/symfony/var-dumper/Caster/LinkStub.php opt/app/vendor/symfony/var-dumper/Caster/PdoCaster.php opt/app/vendor/symfony/var-dumper/Caster/PgSqlCaster.php @@ -5940,18 +6307,32 @@ opt/app/vendor/symfony/var-dumper/Cloner/Data.php opt/app/vendor/symfony/var-dumper/Cloner/DumperInterface.php opt/app/vendor/symfony/var-dumper/Cloner/Stub.php opt/app/vendor/symfony/var-dumper/Cloner/VarCloner.php +opt/app/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php +opt/app/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php +opt/app/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php +opt/app/vendor/symfony/var-dumper/Command/ServerDumpCommand.php opt/app/vendor/symfony/var-dumper/Dumper/AbstractDumper.php opt/app/vendor/symfony/var-dumper/Dumper/CliDumper.php +opt/app/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php +opt/app/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php +opt/app/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php +opt/app/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php opt/app/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php opt/app/vendor/symfony/var-dumper/Dumper/HtmlDumper.php +opt/app/vendor/symfony/var-dumper/Dumper/ServerDumper.php opt/app/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php opt/app/vendor/symfony/var-dumper/LICENSE opt/app/vendor/symfony/var-dumper/README.md +opt/app/vendor/symfony/var-dumper/Resources/bin/var-dump-server +opt/app/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css opt/app/vendor/symfony/var-dumper/Resources/functions/dump.php +opt/app/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js +opt/app/vendor/symfony/var-dumper/Server/DumpServer.php opt/app/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php opt/app/vendor/symfony/var-dumper/Tests/Caster/CasterTest.php opt/app/vendor/symfony/var-dumper/Tests/Caster/DateCasterTest.php opt/app/vendor/symfony/var-dumper/Tests/Caster/ExceptionCasterTest.php +opt/app/vendor/symfony/var-dumper/Tests/Caster/GmpCasterTest.php opt/app/vendor/symfony/var-dumper/Tests/Caster/PdoCasterTest.php opt/app/vendor/symfony/var-dumper/Tests/Caster/RedisCasterTest.php opt/app/vendor/symfony/var-dumper/Tests/Caster/ReflectionCasterTest.php @@ -5962,11 +6343,13 @@ opt/app/vendor/symfony/var-dumper/Tests/Cloner/DataTest.php opt/app/vendor/symfony/var-dumper/Tests/Cloner/VarClonerTest.php opt/app/vendor/symfony/var-dumper/Tests/Dumper/CliDumperTest.php opt/app/vendor/symfony/var-dumper/Tests/Dumper/HtmlDumperTest.php +opt/app/vendor/symfony/var-dumper/Tests/Dumper/ServerDumperTest.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/FooInterface.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/GeneratorDemo.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/NotLoadableClass.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/Twig.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/dumb-var.php +opt/app/vendor/symfony/var-dumper/Tests/Fixtures/dump_server.php opt/app/vendor/symfony/var-dumper/Tests/Fixtures/xml_reader.xml opt/app/vendor/symfony/var-dumper/Tests/Test/VarDumperTestTraitTest.php opt/app/vendor/symfony/var-dumper/VarDumper.php diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 6f5c532bd3..3f86b4e979 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = ( manifest = ( appTitle = (defaultText = "Firefly III"), - appVersion = 13, - appMarketingVersion = (defaultText = "4.7.4"), + appVersion = 14, + appMarketingVersion = (defaultText = "4.7.5"), actions = [ # Define your "new document" handlers here. @@ -65,9 +65,9 @@ const pkgdef :Spk.PackageDefinition = ( # Sizes are given in device-independent pixels, so if you took these # screenshots on a Retina-style high DPI screen, divide each dimension by two. - (width = 1291, height = 800, png = embed "screenshots/screenshot-1.png"), - (width = 1291, height = 800, png = embed "screenshots/screenshot-2.png"), - (width = 1291, height = 800, png = embed "screenshots/screenshot-3.png"), + (width = 1290, height = 800, png = embed "screenshots/screenshot-1.png"), + (width = 1290, height = 800, png = embed "screenshots/screenshot-2.png"), + (width = 1290, height = 800, png = embed "screenshots/screenshot-3.png"), ], changeLog = (defaultText = embed "changelog.md"), diff --git a/.sandstorm/screenshots/screenshot-1.png b/.sandstorm/screenshots/screenshot-1.png index c8a49481c0..5f786ad2ff 100644 Binary files a/.sandstorm/screenshots/screenshot-1.png and b/.sandstorm/screenshots/screenshot-1.png differ diff --git a/.sandstorm/screenshots/screenshot-2.png b/.sandstorm/screenshots/screenshot-2.png index 15dbbd4122..fe0bfb0218 100644 Binary files a/.sandstorm/screenshots/screenshot-2.png and b/.sandstorm/screenshots/screenshot-2.png differ diff --git a/.sandstorm/screenshots/screenshot-3.png b/.sandstorm/screenshots/screenshot-3.png index c52941aa7b..f68d5de0c1 100644 Binary files a/.sandstorm/screenshots/screenshot-3.png and b/.sandstorm/screenshots/screenshot-3.png differ diff --git a/.travis.yml b/.travis.yml index 4543dc5049..0771080d51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 7.1 - - 7.2 + - 7.1.18 cache: directories: @@ -14,7 +13,6 @@ install: - cp .env.testing .env - php artisan clear-compiled - php artisan env - - cp .env.testing .env - wget -q https://github.com/firefly-iii/test-data/raw/master/storage/database.sqlite -O storage/database/database.sqlite - mkdir -p build/logs diff --git a/Dockerfile b/Dockerfile index e9619b7ee4..4eb3f9cb1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ RUN apt-get update -y && \ libpq-dev \ libbz2-dev \ gettext-base \ + cron \ locales && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -47,6 +48,13 @@ RUN cd /tmp && \ make && \ make install + +# Create the log file to be able to run tail +RUN touch /var/log/cron.log + +# Setup cron job +RUN (crontab -l ; echo "* * * * * root $FIREFLY_PATH/artisan schedule:run >> /var/log/cron.log") | crontab + # Install PHP exentions. RUN docker-php-ext-install -j$(nproc) curl gd intl json readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 pdo_pgsql @@ -77,6 +85,9 @@ RUN composer install --prefer-dist --no-dev --no-scripts --no-suggest # Expose port 80 EXPOSE 80 +# Run the command on container startup +CMD cron + # Run entrypoint thing ENTRYPOINT [".deploy/docker/entrypoint.sh"] diff --git a/app/Api/V1/Controllers/AboutController.php b/app/Api/V1/Controllers/AboutController.php index 77e63b7c64..2f7795d05c 100644 --- a/app/Api/V1/Controllers/AboutController.php +++ b/app/Api/V1/Controllers/AboutController.php @@ -26,20 +26,25 @@ namespace FireflyIII\Api\V1\Controllers; use DB; use FireflyIII\Transformers\UserTransformer; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use League\Fractal\Manager; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; /** + * Returns basic information about this installation. + * * Class AboutController */ class AboutController extends Controller { /** - * @return \Illuminate\Http\JsonResponse + * Returns system information. + * + * @return JsonResponse */ - public function about() + public function about(): JsonResponse { $search = ['~', '#']; $replace = ['\~', '# ']; @@ -59,11 +64,13 @@ class AboutController extends Controller } /** + * Returns information about the user. + * * @param Request $request * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ - public function user(Request $request) + public function user(Request $request): JsonResponse { $manager = new Manager(); $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; diff --git a/app/Api/V1/Controllers/AccountController.php b/app/Api/V1/Controllers/AccountController.php index 2f951fed3b..f7bd0580f2 100644 --- a/app/Api/V1/Controllers/AccountController.php +++ b/app/Api/V1/Controllers/AccountController.php @@ -30,6 +30,8 @@ use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Transformers\AccountTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Manager; @@ -37,7 +39,6 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; -use Preferences; /** * Class AccountController @@ -51,20 +52,20 @@ class AccountController extends Controller /** * AccountController constructor. - * - * @throws \FireflyIII\Exceptions\FireflyException */ public function __construct() { parent::__construct(); $this->middleware( function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); // @var AccountRepositoryInterface repository $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser(auth()->user()); + $this->repository->setUser($user); $this->currencyRepository = app(CurrencyRepositoryInterface::class); - $this->currencyRepository->setUser(auth()->user()); + $this->currencyRepository->setUser($user); return $next($request); } @@ -76,9 +77,9 @@ class AccountController extends Controller * * @param \FireflyIII\Models\Account $account * - * @return \Illuminate\Http\Response + * @return JsonResponse */ - public function delete(Account $account) + public function delete(Account $account): JsonResponse { $this->repository->destroy($account, null); @@ -90,12 +91,12 @@ class AccountController extends Controller * * @param Request $request * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ - public function index(Request $request) + public function index(Request $request): JsonResponse { // create some objects: - $manager = new Manager(); + $manager = new Manager; $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; // read type from URI @@ -104,7 +105,7 @@ class AccountController extends Controller // types to get, page size: $types = $this->mapTypes($this->parameters->get('type')); - $pageSize = (int)Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; // get list of accounts. Count it and split it. $collection = $this->repository->getAccountsByType($types); @@ -129,9 +130,9 @@ class AccountController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function show(Request $request, Account $account) + public function show(Request $request, Account $account): JsonResponse { - $manager = new Manager(); + $manager = new Manager; // add include parameter: $include = $request->get('include') ?? ''; @@ -149,7 +150,7 @@ class AccountController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function store(AccountRequest $request) + public function store(AccountRequest $request): JsonResponse { $data = $request->getAll(); // if currency ID is 0, find the currency by the code: @@ -158,7 +159,7 @@ class AccountController extends Controller $data['currency_id'] = null === $currency ? 0 : $currency->id; } $account = $this->repository->store($data); - $manager = new Manager(); + $manager = new Manager; $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); @@ -175,7 +176,7 @@ class AccountController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function update(AccountRequest $request, Account $account) + public function update(AccountRequest $request, Account $account): JsonResponse { $data = $request->getAll(); // if currency ID is 0, find the currency by the code: @@ -186,7 +187,7 @@ class AccountController extends Controller // set correct type: $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); $this->repository->update($account, $data); - $manager = new Manager(); + $manager = new Manager; $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); diff --git a/app/Api/V1/Controllers/AttachmentController.php b/app/Api/V1/Controllers/AttachmentController.php new file mode 100644 index 0000000000..8dae717b38 --- /dev/null +++ b/app/Api/V1/Controllers/AttachmentController.php @@ -0,0 +1,229 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\AttachmentRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; +use FireflyIII\Models\Attachment; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; +use FireflyIII\Transformers\AttachmentTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Http\Response as LaravelResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * Class AttachmentController + */ +class AttachmentController extends Controller +{ + /** @var AttachmentRepositoryInterface */ + private $repository; + + /** + * AccountController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(AttachmentRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function delete(Attachment $attachment): JsonResponse + { + $this->repository->destroy($attachment); + + return response()->json([], 204); + } + + /** + * @param Attachment $attachment + * + * @return LaravelResponse + * @throws FireflyException + */ + public function download(Attachment $attachment): LaravelResponse + { + if ($attachment->uploaded === false) { + throw new FireflyException('No file has been uploaded for this attachment (yet).'); + } + if ($this->repository->exists($attachment)) { + $content = $this->repository->getContent($attachment); + $quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); + + /** @var LaravelResponse $response */ + $response = response($content, 200); + $response + ->header('Content-Description', 'File Transfer') + ->header('Content-Type', 'application/octet-stream') + ->header('Content-Disposition', 'attachment; filename=' . $quoted) + ->header('Content-Transfer-Encoding', 'binary') + ->header('Connection', 'Keep-Alive') + ->header('Expires', '0') + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', \strlen($content)); + + return $response; + } + throw new FireflyException('Could not find the indicated attachment. The file is no longer there.'); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of accounts. Count it and split it. + $collection = $this->repository->get(); + $count = $collection->count(); + $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.attachments.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($attachments, new AttachmentTransformer($this->parameters), 'attachments'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Display the specified resource. + * + * @param Request $request + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function show(Request $request, Attachment $attachment): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Store a newly created resource in storage. + * + * @param AttachmentRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(AttachmentRequest $request): JsonResponse + { + $data = $request->getAll(); + $attachment = $this->repository->store($data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Update the specified resource in storage. + * + * @param AttachmentRequest $request + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function update(AttachmentRequest $request, Attachment $attachment): JsonResponse + { + $data = $request->getAll(); + $this->repository->update($attachment, $data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($attachment, new AttachmentTransformer($this->parameters), 'attachments'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param Request $request + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function upload(Request $request, Attachment $attachment): JsonResponse + { + /** @var AttachmentHelperInterface $helper */ + $helper = app(AttachmentHelperInterface::class); + $body = $request->getContent(); + $helper->saveAttachmentFromApi($attachment, $body); + + return response()->json([], 204); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/AvailableBudgetController.php b/app/Api/V1/Controllers/AvailableBudgetController.php new file mode 100644 index 0000000000..ee182bb457 --- /dev/null +++ b/app/Api/V1/Controllers/AvailableBudgetController.php @@ -0,0 +1,183 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\AvailableBudgetRequest; +use FireflyIII\Models\AvailableBudget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Transformers\AvailableBudgetTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * Class AvailableBudgetController + */ +class AvailableBudgetController extends Controller +{ + /** @var CurrencyRepositoryInterface */ + private $currencyRepository; + /** @var BudgetRepositoryInterface */ + private $repository; + + /** + * AccountController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(BudgetRepositoryInterface::class); + $this->currencyRepository = app(CurrencyRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param AvailableBudget $availableBudget + * + * @return JsonResponse + */ + public function delete(AvailableBudget $availableBudget): JsonResponse + { + $this->repository->destroyAvailableBudget($availableBudget); + + return response()->json([], 204); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of available budgets. Count it and split it. + $collection = $this->repository->getAvailableBudgets(); + $count = $collection->count(); + $availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.available_budgets.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($availableBudgets, new AvailableBudgetTransformer($this->parameters), 'available_budgets'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Display the specified resource. + * + * @param Request $request + * @param AvailableBudget $availableBudget + * + * @return JsonResponse + */ + public function show(Request $request, AvailableBudget $availableBudget): JsonResponse + { + + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Store a newly created resource in storage. + * + * @param AvailableBudgetRequest $request + * + * @return JsonResponse + */ + public function store(AvailableBudgetRequest $request): JsonResponse + { + $data = $request->getAll(); + $currency = $this->currencyRepository->findNull($data['transaction_currency_id']); + $availableBudget = $this->repository->setAvailableBudget($currency, $data['start_date'], $data['end_date'], $data['amount']); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Update the specified resource in storage. + * + * @param AvailableBudgetRequest $request + * @param AvailableBudget $availableBudget + * + * @return JsonResponse + */ + public function update(AvailableBudgetRequest $request, AvailableBudget $availableBudget): JsonResponse + { + $data = $request->getAll(); + $this->repository->updateAvailableBudget($availableBudget, $data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/BillController.php b/app/Api/V1/Controllers/BillController.php index 420b01dc16..c06b060df9 100644 --- a/app/Api/V1/Controllers/BillController.php +++ b/app/Api/V1/Controllers/BillController.php @@ -37,7 +37,6 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; -use Preferences; /** * Class BillController @@ -49,8 +48,6 @@ class BillController extends Controller /** * BillController constructor. - * - * @throws FireflyException */ public function __construct() { @@ -89,7 +86,7 @@ class BillController extends Controller */ public function index(Request $request): JsonResponse { - $pageSize = (int)Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $paginator = $this->repository->getPaginator($pageSize); /** @var Collection $bills */ $bills = $paginator->getCollection(); diff --git a/app/Api/V1/Controllers/BudgetController.php b/app/Api/V1/Controllers/BudgetController.php new file mode 100644 index 0000000000..d2a2ba2041 --- /dev/null +++ b/app/Api/V1/Controllers/BudgetController.php @@ -0,0 +1,176 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\BudgetRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * Class BudgetController + */ +class BudgetController extends Controller +{ + /** @var BudgetRepositoryInterface */ + private $repository; + + /** + * BudgetController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var BudgetRepositoryInterface repository */ + $this->repository = app(BudgetRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Budget $budget + * + * @return JsonResponse + */ + public function delete(Budget $budget): JsonResponse + { + $this->repository->destroy($budget); + + return response()->json([], 204); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getBudgets(); + $count = $collection->count(); + $budgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budgets.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($budgets, new BudgetTransformer($this->parameters), 'budgets'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + + /** + * @param Request $request + * @param Budget $budget + * + * @return JsonResponse + */ + public function show(Request $request, Budget $budget): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param BudgetRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(BudgetRequest $request): JsonResponse + { + $budget = $this->repository->store($request->getAll()); + if (null !== $budget) { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + throw new FireflyException('Could not store new budget.'); // @codeCoverageIgnore + } + + + /** + * @param BudgetRequest $request + * @param Budget $budget + * + * @return JsonResponse + */ + public function update(BudgetRequest $request, Budget $budget): JsonResponse + { + $data = $request->getAll(); + $budget = $this->repository->update($budget, $data); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($budget, new BudgetTransformer($this->parameters), 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/BudgetLimitController.php b/app/Api/V1/Controllers/BudgetLimitController.php new file mode 100644 index 0000000000..fe07060205 --- /dev/null +++ b/app/Api/V1/Controllers/BudgetLimitController.php @@ -0,0 +1,232 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use Carbon\Carbon; +use Exception; +use FireflyIII\Api\V1\Requests\AvailableBudgetRequest; +use FireflyIII\Api\V1\Requests\BudgetLimitRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetLimitTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use InvalidArgumentException; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; +use Log; +use Throwable; + +/** + * Class BudgetLimitController + */ +class BudgetLimitController extends Controller +{ + ///** @var CurrencyRepositoryInterface */ + //private $currencyRepository; + /** @var BudgetRepositoryInterface */ + private $repository; + + /** + * AccountController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(BudgetRepositoryInterface::class); + //$this->currencyRepository = app(CurrencyRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + */ + public function delete(BudgetLimit $budgetLimit): JsonResponse + { + $this->repository->destroyBudgetLimit($budgetLimit); + + return response()->json([], 204); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // read budget from request + $budgetId = (int)($request->get('budget_id') ?? 0); + $budget = null; + if ($budgetId > 0) { + $budget = $this->repository->findNull($budgetId); + } + // read start date from request + $start = null; + try { + $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); + $this->parameters->set('start', $start->format('Y-m-d')); + } catch (InvalidArgumentException $e) { + Log::debug(sprintf('Could not parse start date "%s": %s', $request->get('start'), $e->getMessage())); + + } + + // read end date from request + $end = null; + try { + $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); + $this->parameters->set('end', $end->format('Y-m-d')); + } catch (InvalidArgumentException $e) { + Log::debug(sprintf('Could not parse end date "%s": %s', $request->get('end'), $e->getMessage())); + } + $this->parameters->set('budget_id', $budgetId); + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budget limits. Count it and split it. + $collection = new Collection; + if (null === $budget) { + $collection = $this->repository->getAllBudgetLimits($start, $end); + } + if (null !== $budget) { + $collection = $this->repository->getBudgetLimits($budget, $start, $end); + } + + $count = $collection->count(); + $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($budgetLimits, new BudgetLimitTransformer($this->parameters), 'budget_limits'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Display the specified resource. + * + * @param Request $request + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + */ + public function show(Request $request, BudgetLimit $budgetLimit): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Store a newly created resource in storage. + * + * @param BudgetLimitRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(BudgetLimitRequest $request): JsonResponse + { + $data = $request->getAll(); + $budget = $this->repository->findNull($data['budget_id']); + if (null === $budget) { + throw new FireflyException('Unknown budget.'); + } + $data['budget'] = $budget; + $budgetLimit = $this->repository->storeBudgetLimit($data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * Update the specified resource in storage. + * + * @param AvailableBudgetRequest $request + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + */ + public function update(BudgetLimitRequest $request, BudgetLimit $budgetLimit): JsonResponse + { + $data = $request->getAll(); + $budget = $this->repository->findNull($data['budget_id']); + if (null === $budget) { + $budget = $budgetLimit->budget; + } + $data['budget'] = $budget; + $budgetLimit = $this->repository->updateBudgetLimit($budgetLimit, $data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($budgetLimit, new BudgetLimitTransformer($this->parameters), 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/CategoryController.php b/app/Api/V1/Controllers/CategoryController.php new file mode 100644 index 0000000000..fc33a6fe16 --- /dev/null +++ b/app/Api/V1/Controllers/CategoryController.php @@ -0,0 +1,176 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\CategoryRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Transformers\CategoryTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * Class CategoryController + */ +class CategoryController extends Controller +{ + /** @var CategoryRepositoryInterface */ + private $repository; + + /** + * CategoryController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var CategoryRepositoryInterface repository */ + $this->repository = app(CategoryRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Category $category + * + * @return JsonResponse + */ + public function delete(Category $category): JsonResponse + { + $this->repository->destroy($category); + + return response()->json([], 204); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getCategories(); + $count = $collection->count(); + $categories = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.categories.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($categories, new CategoryTransformer($this->parameters), 'categories'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + + /** + * @param Request $request + * @param Category $category + * + * @return JsonResponse + */ + public function show(Request $request, Category $category): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($category, new CategoryTransformer($this->parameters), 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param CategoryRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(CategoryRequest $request): JsonResponse + { + $category = $this->repository->store($request->getAll()); + if (null !== $category) { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($category, new CategoryTransformer($this->parameters), 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + throw new FireflyException('Could not store new category.'); // @codeCoverageIgnore + } + + + /** + * @param CategoryRequest $request + * @param Category $category + * + * @return JsonResponse + */ + public function update(CategoryRequest $request, Category $category): JsonResponse + { + $data = $request->getAll(); + $category = $this->repository->update($category, $data); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($category, new CategoryTransformer($this->parameters), 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/ConfigurationController.php b/app/Api/V1/Controllers/ConfigurationController.php new file mode 100644 index 0000000000..9bedd0a459 --- /dev/null +++ b/app/Api/V1/Controllers/ConfigurationController.php @@ -0,0 +1,105 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Configuration; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; + +/** + * Class ConfigurationController + */ +class ConfigurationController extends Controller +{ + + /** + * @throws FireflyException + */ + public function index() + { + if (!auth()->user()->hasRole('owner')) { + throw new FireflyException('No access to method.'); // @codeCoverageIgnore + } + $configData = $this->getConfigData(); + + return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param Request $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function update(Request $request): JsonResponse + { + if (!auth()->user()->hasRole('owner')) { + throw new FireflyException('No access to method.'); // @codeCoverageIgnore + } + $name = $request->get('name'); + $value = $request->get('value'); + $valid = ['is_demo_site', 'permission_update_check', 'single_user_mode']; + if (!\in_array($name, $valid, true)) { + throw new FireflyException('You cannot edit this configuration value.'); + } + $configValue = ''; + switch ($name) { + case 'is_demo_site': + case 'single_user_mode': + $configValue = $value === 'true'; + break; + case 'permission_update_check': + $configValue = (int)$value >= -1 && (int)$value <= 1 ? (int)$value : -1; + break; + } + app('fireflyconfig')->set($name, $configValue); + $configData = $this->getConfigData(); + + return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @return array + */ + private function getConfigData(): array + { + /** @var Configuration $isDemoSite */ + $isDemoSite = app('fireflyconfig')->get('is_demo_site'); + /** @var Configuration $updateCheck */ + $updateCheck = app('fireflyconfig')->get('permission_update_check'); + /** @var Configuration $lastCheck */ + $lastCheck = app('fireflyconfig')->get('last_update_check'); + /** @var Configuration $singleUser */ + $singleUser = app('fireflyconfig')->get('single_user_mode'); + $data = [ + 'is_demo_site' => null === $isDemoSite ? null : $isDemoSite->data, + 'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data, + 'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data, + 'single_user_mode' => null === $singleUser ? null : $singleUser->data, + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index a212ad3029..3b5b3d23b0 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -26,8 +26,6 @@ namespace FireflyIII\Api\V1\Controllers; use Carbon\Carbon; use Carbon\Exceptions\InvalidDateException; -use FireflyConfig; -use FireflyIII\Exceptions\FireflyException; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; diff --git a/app/Api/V1/Controllers/CurrencyController.php b/app/Api/V1/Controllers/CurrencyController.php index 176986afb8..174c4ff261 100644 --- a/app/Api/V1/Controllers/CurrencyController.php +++ b/app/Api/V1/Controllers/CurrencyController.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\BillRequest; use FireflyIII\Api\V1\Requests\CurrencyRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionCurrency; @@ -39,7 +38,6 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; -use Preferences; /** * Class CurrencyController @@ -102,7 +100,7 @@ class CurrencyController extends Controller */ public function index(Request $request): JsonResponse { - $pageSize = (int)Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $collection = $this->repository->get(); $count = $collection->count(); // slice them: @@ -158,8 +156,8 @@ class CurrencyController extends Controller $currency = $this->repository->store($request->getAll()); if ($request->boolean('default') === true) { - Preferences::set('currencyPreference', $currency->code); - Preferences::mark(); + app('preferences')->set('currencyPreference', $currency->code); + app('preferences')->mark(); } if (null !== $currency) { $manager = new Manager(); @@ -178,7 +176,7 @@ class CurrencyController extends Controller /** - * @param CurrencyRequest $request + * @param CurrencyRequest $request * @param TransactionCurrency $currency * * @return JsonResponse @@ -189,8 +187,8 @@ class CurrencyController extends Controller $currency = $this->repository->update($currency, $data); if ($request->boolean('default') === true) { - Preferences::set('currencyPreference', $currency->code); - Preferences::mark(); + app('preferences')->set('currencyPreference', $currency->code); + app('preferences')->mark(); } $manager = new Manager(); diff --git a/app/Api/V1/Controllers/CurrencyExchangeRateController.php b/app/Api/V1/Controllers/CurrencyExchangeRateController.php new file mode 100644 index 0000000000..54d8672b0e --- /dev/null +++ b/app/Api/V1/Controllers/CurrencyExchangeRateController.php @@ -0,0 +1,115 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Services\Currency\ExchangeRateInterface; +use FireflyIII\Transformers\CurrencyExchangeRateTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use InvalidArgumentException; +use League\Fractal\Manager; +use League\Fractal\Resource\Item; +use Log; + +/** + * + * Class CurrencyExchangeRateController + */ +class CurrencyExchangeRateController extends Controller +{ + /** @var CurrencyRepositoryInterface */ + private $repository; + + /** + * CurrencyExchangeRateController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(CurrencyRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + + } + + /** + * @param Request $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // currencies + $fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR'); + $toCurrency = $this->repository->findByCodeNull($request->get('to') ?? 'USD'); + + if (null === $fromCurrency) { + throw new FireflyException('Unknown source currency.'); + } + if (null === $toCurrency) { + throw new FireflyException('Unknown destination currency.'); + } + + $dateObj = new Carbon; + try { + $dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d')); + } catch (InvalidArgumentException $e) { + Log::debug($e->getMessage()); + } + + + $this->parameters->set('from', $fromCurrency->code); + $this->parameters->set('to', $toCurrency->code); + $this->parameters->set('date', $dateObj->format('Y-m-d')); + + // get the exchange rate. + $rate = $this->repository->getExchangeRate($fromCurrency, $toCurrency, $dateObj); + if (null === $rate) { + // create service: + /** @var ExchangeRateInterface $service */ + $service = app(ExchangeRateInterface::class); + $service->setUser(auth()->user()); + + // get rate: + $rate = $service->getRate($fromCurrency, $toCurrency, $dateObj); + } + + $resource = new Item($rate, new CurrencyExchangeRateTransformer($this->parameters), 'currency_exchange_rates'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/JournalLinkController.php b/app/Api/V1/Controllers/JournalLinkController.php new file mode 100644 index 0000000000..0dde9742be --- /dev/null +++ b/app/Api/V1/Controllers/JournalLinkController.php @@ -0,0 +1,207 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\JournalLinkRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionJournalLink; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; +use FireflyIII\Transformers\JournalLinkTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +class JournalLinkController extends Controller +{ + /** @var JournalRepositoryInterface */ + private $journalRepository; + /** @var LinkTypeRepositoryInterface */ + private $repository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->journalRepository = app(JournalRepositoryInterface::class); + + $this->repository->setUser($user); + $this->journalRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param TransactionJournalLink $link + * + * @return JsonResponse + */ + public function delete(TransactionJournalLink $link): JsonResponse + { + $this->repository->destroyLink($link); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // read type from URI + $name = $request->get('name') ?? null; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + $linkType = $this->repository->findByName($name); + + // get list of accounts. Count it and split it. + $collection = $this->repository->getJournalLinks($linkType); + $count = $collection->count(); + $journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.journal_links.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($journalLinks, new JournalLinkTransformer($this->parameters), 'journal_links'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * List single resource. + * + * @param Request $request + * @param TransactionJournalLink $journalLink + * + * @return JsonResponse + */ + public function show(Request $request, TransactionJournalLink $journalLink): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * Store new object. + * + * @param JournalLinkRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(JournalLinkRequest $request): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $data = $request->getAll(); + $inward = $this->journalRepository->findNull($data['inward_id'] ?? 0); + $outward = $this->journalRepository->findNull($data['outward_id'] ?? 0); + if (null === $inward || null === $outward) { + throw new FireflyException('Source or destination is NULL.'); + } + $data['direction'] = 'inward'; + + $journalLink = $this->repository->storeLink($data, $inward, $outward); + + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * @param JournalLinkRequest $request + * @param TransactionJournalLink $journalLink + * + * @return JsonResponse + * @throws FireflyException + */ + public function update(JournalLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + + $data = $request->getAll(); + $data['inward'] = $this->journalRepository->findNull($data['inward_id'] ?? 0); + $data['outward'] = $this->journalRepository->findNull($data['outward_id'] ?? 0); + if (null === $data['inward'] || null === $data['outward']) { + throw new FireflyException('Source or destination is NULL.'); + } + $data['direction'] = 'inward'; + $journalLink = $this->repository->updateLink($journalLink, $data); + + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/LinkTypeController.php b/app/Api/V1/Controllers/LinkTypeController.php new file mode 100644 index 0000000000..31215cd5a3 --- /dev/null +++ b/app/Api/V1/Controllers/LinkTypeController.php @@ -0,0 +1,197 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\LinkTypeRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\LinkType; +use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Transformers\LinkTypeTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * + * Class LinkTypeController + */ +class LinkTypeController extends Controller +{ + /** @var LinkTypeRepositoryInterface */ + private $repository; + + /** @var UserRepositoryInterface */ + private $userRepository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param LinkType $linkType + * + * @return JsonResponse + * @throws FireflyException + */ + public function delete(LinkType $linkType): JsonResponse + { + if ($linkType->editable === false) { + throw new FireflyException(sprintf('You cannot delete this link type (#%d, "%s")', $linkType->id, $linkType->name)); + } + $this->repository->destroy($linkType, null); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of accounts. Count it and split it. + $collection = $this->repository->get(); + $count = $collection->count(); + $linkTypes = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.link_types.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($linkTypes, new LinkTypeTransformer($this->parameters), 'link_types'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * List single resource. + * + * @param Request $request + * @param LinkType $linkType + * + * @return JsonResponse + */ + public function show(Request $request, LinkType $linkType): JsonResponse + { + $manager = new Manager; + + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * Store new object. + * + * @param LinkTypeRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(LinkTypeRequest $request): JsonResponse + { + if (!$this->userRepository->hasRole(auth()->user(), 'owner')) { + throw new FireflyException('You need the "owner"-role to do this.'); + } + $data = $request->getAll(); + // if currency ID is 0, find the currency by the code: + $linkType = $this->repository->store($data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * @param LinkTypeRequest $request + * @param LinkType $linkType + * + * @return JsonResponse + * @throws FireflyException + */ + public function update(LinkTypeRequest $request, LinkType $linkType): JsonResponse + { + if ($linkType->editable === false) { + throw new FireflyException(sprintf('You cannot edit this link type (#%d, "%s")', $linkType->id, $linkType->name)); + } + if (!$this->userRepository->hasRole(auth()->user(), 'owner')) { + throw new FireflyException('You need the "owner"-role to do this.'); + } + + $data = $request->getAll(); + $this->repository->update($linkType, $data); + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($linkType, new LinkTypeTransformer($this->parameters), 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/PiggyBankController.php b/app/Api/V1/Controllers/PiggyBankController.php new file mode 100644 index 0000000000..2dd1f0f8a6 --- /dev/null +++ b/app/Api/V1/Controllers/PiggyBankController.php @@ -0,0 +1,180 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\PiggyBankRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\PiggyBankTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * TODO order up and down. + * Class PiggyBankController + */ +class PiggyBankController extends Controller +{ + + /** @var PiggyBankRepositoryInterface */ + private $repository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(PiggyBankRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param PiggyBank $piggyBank + * + * @return JsonResponse + */ + public function delete(PiggyBank $piggyBank): JsonResponse + { + $this->repository->destroy($piggyBank); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getPiggyBanks(); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($piggyBanks, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * List single resource. + * + * @param Request $request + * @param PiggyBank $piggyBank + * + * @return JsonResponse + */ + public function show(Request $request, PiggyBank $piggyBank): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * Store new object. + * + * @param PiggyBankRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(PiggyBankRequest $request): JsonResponse + { + $piggyBank = $this->repository->store($request->getAll()); + if (null !== $piggyBank) { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + throw new FireflyException('Could not store new piggy bank.'); // @codeCoverageIgnore + + } + + /** + * @param PiggyBankRequest $request + * @param PiggyBank $piggyBank + * + * @return JsonResponse + */ + public function update(PiggyBankRequest $request, PiggyBank $piggyBank): JsonResponse + { + $piggyBank = $this->repository->update($piggyBank, $request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/PreferenceController.php b/app/Api/V1/Controllers/PreferenceController.php new file mode 100644 index 0000000000..3efe8fa35a --- /dev/null +++ b/app/Api/V1/Controllers/PreferenceController.php @@ -0,0 +1,157 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\PreferenceRequest; +use FireflyIII\Models\Preference; +use FireflyIII\Transformers\PreferenceTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Support\Collection; +use League\Fractal\Manager; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; +use Preferences; + +/** + * + * Class PreferenceController + */ +class PreferenceController extends Controller +{ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + // todo add local repositories. + return $next($request); + } + ); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + /** @var User $user */ + $user = auth()->user(); + $available = [ + 'language', 'customFiscalYear', 'fiscalYearStart', 'currencyPreference', + 'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange', + 'listPageSize, twoFactorAuthEnabled', + ]; + $preferences = new Collection; + foreach ($available as $name) { + $pref = Preferences::getForUser($user, $name); + if (null !== $pref) { + $preferences->push($pref); + } + } + + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($preferences, new PreferenceTransformer($this->parameters), 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + + } + + /** + * List single resource. + * + * @param Request $request + * @param Preference $preference + * + * @return JsonResponse + */ + public function show(Request $request, Preference $preference): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($preference, new PreferenceTransformer($this->parameters), 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * @param PreferenceRequest $request + * @param Preference $preference + * + * @return JsonResponse + */ + public function update(PreferenceRequest $request, Preference $preference): JsonResponse + { + + $data = $request->getAll(); + $newValue = $data['data']; + switch ($preference->name) { + default: + break; + case 'transaction_journal_optional_fields': + case 'frontPageAccounts': + $newValue = explode(',', $data['data']); + break; + case 'listPageSize': + $newValue = (int)$data['data']; + break; + case 'customFiscalYear': + case 'twoFactorAuthEnabled': + $newValue = (int)$data['data'] === 1; + break; + } + $result = Preferences::set($preference->name, $newValue); + + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($result, new PreferenceTransformer($this->parameters), 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/RecurrenceController.php b/app/Api/V1/Controllers/RecurrenceController.php new file mode 100644 index 0000000000..a5ec34e9c6 --- /dev/null +++ b/app/Api/V1/Controllers/RecurrenceController.php @@ -0,0 +1,179 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\RecurrenceRequest; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * + * Class RecurrenceController + */ +class RecurrenceController extends Controller +{ + /** @var RecurringRepositoryInterface */ + private $repository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param Recurrence $recurrence + * + * @return JsonResponse + */ + public function delete(Recurrence $recurrence): JsonResponse + { + $this->repository->destroy($recurrence); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getAll(); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($piggyBanks, new RecurrenceTransformer($this->parameters), 'recurrences'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * List single resource. + * + * @param Request $request + * @param Recurrence $recurrence + * + * @return JsonResponse + */ + public function show(Request $request, Recurrence $recurrence): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($recurrence, new RecurrenceTransformer($this->parameters), 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + + } + + /** + * Store new object. + * + * @param RecurrenceRequest $request + * + * @return JsonResponse + */ + public function store(RecurrenceRequest $request): JsonResponse + { + $recurrence = $this->repository->store($request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new Item($recurrence, new RecurrenceTransformer($this->parameters), 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param RecurrenceRequest $request + * @param Recurrence $recurrence + * + * @return JsonResponse + */ + public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse + { + $data = $request->getAll(); + + // + + $category = $this->repository->update($recurrence, $data); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($category, new RecurrenceTransformer($this->parameters), 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/RuleController.php b/app/Api/V1/Controllers/RuleController.php new file mode 100644 index 0000000000..bd581dcfb2 --- /dev/null +++ b/app/Api/V1/Controllers/RuleController.php @@ -0,0 +1,173 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\RuleRequest; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + +/** + * Class RuleController + */ +class RuleController extends Controller +{ + /** @var RuleRepositoryInterface */ + private $ruleRepository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param Rule $rule + * + * @return JsonResponse + */ + public function delete(Rule $rule): JsonResponse + { + $this->ruleRepository->destroy($rule); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->ruleRepository->getAll(); + $count = $collection->count(); + $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($rules, new RuleTransformer($this->parameters), 'rules'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * List single resource. + * + * @param Request $request + * @param Rule $rule + * + * @return JsonResponse + */ + public function show(Request $request, Rule $rule): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($rule, new RuleTransformer($this->parameters), 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * Store new object. + * + * @param Request $request + * + * @return JsonResponse + */ + public function store(RuleRequest $request): JsonResponse + { + $rule = $this->ruleRepository->store($request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($rule, new RuleTransformer($this->parameters), 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param RuleRequest $request + * @param Rule $rule + * + * @return JsonResponse + */ + public function update(RuleRequest $request, Rule $rule): JsonResponse + { + $rule = $this->ruleRepository->update($rule, $request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($rule, new RuleTransformer($this->parameters), 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/RuleGroupController.php b/app/Api/V1/Controllers/RuleGroupController.php new file mode 100644 index 0000000000..9085594c69 --- /dev/null +++ b/app/Api/V1/Controllers/RuleGroupController.php @@ -0,0 +1,171 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\RuleGroupRequest; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Transformers\RuleGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Manager; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; + + +class RuleGroupController extends Controller +{ + /** @var RuleGroupRepositoryInterface */ + private $ruleGroupRepository; + + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param string $object + * + * @return JsonResponse + */ + public function delete(RuleGroup $ruleGroup): JsonResponse + { + $this->ruleGroupRepository->destroy($ruleGroup, null); + + return response()->json([], 204); + } + + /** + * List all of them. + * + * @param Request $request + * + * @return JsonResponse] + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->ruleGroupRepository->get(); + $count = $collection->count(); + $ruleGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rule_groups.index') . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($ruleGroups, new RuleGroupTransformer($this->parameters), 'rule_groups'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * List single resource. + * + * @param Request $request + * @param RuleGroup $ruleGroup + * + * @return JsonResponse + */ + public function show(Request $request, RuleGroup $ruleGroup): JsonResponse + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * Store new object. + * + * @param RuleGroupRequest $request + * + * @return JsonResponse + */ + public function store(RuleGroupRequest $request): JsonResponse + { + $ruleGroup = $this->ruleGroupRepository->store($request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + + /** + * @param Request $request + * @param string $object + * + * @return JsonResponse + */ + public function update(RuleGroupRequest $request, RuleGroup $ruleGroup): JsonResponse + { + $ruleGroup = $this->ruleGroupRepository->update($ruleGroup, $request->getAll()); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/TransactionController.php b/app/Api/V1/Controllers/TransactionController.php index f0dbc2b246..a03b594f42 100644 --- a/app/Api/V1/Controllers/TransactionController.php +++ b/app/Api/V1/Controllers/TransactionController.php @@ -40,7 +40,6 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Serializer\JsonApiSerializer; use Log; -use Preferences; /** * Class TransactionController @@ -92,7 +91,7 @@ class TransactionController extends Controller */ public function index(Request $request) { - $pageSize = (int)Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; // read type from URI $type = $request->get('type') ?? 'default'; @@ -136,17 +135,19 @@ class TransactionController extends Controller /** * @param Request $request * @param Transaction $transaction + * @param string $include * * @return \Illuminate\Http\JsonResponse */ - public function show(Request $request, Transaction $transaction) + public function show(Request $request, Transaction $transaction, string $include = null) { $manager = new Manager(); $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); // add include parameter: - $include = $request->get('include') ?? ''; + $include = $include ?? ''; + $include = $request->get('include') ?? $include; $manager->parseIncludes($include); // collect transactions using the journal collector diff --git a/app/Api/V1/Controllers/UserController.php b/app/Api/V1/Controllers/UserController.php index f2759f9e71..e8466818f9 100644 --- a/app/Api/V1/Controllers/UserController.php +++ b/app/Api/V1/Controllers/UserController.php @@ -36,7 +36,6 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; -use Preferences; /** @@ -94,7 +93,7 @@ class UserController extends Controller public function index(Request $request) { // user preferences - $pageSize = (int)Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; // make manager $manager = new Manager(); diff --git a/app/Api/V1/Requests/AttachmentRequest.php b/app/Api/V1/Requests/AttachmentRequest.php new file mode 100644 index 0000000000..30da4d2683 --- /dev/null +++ b/app/Api/V1/Requests/AttachmentRequest.php @@ -0,0 +1,92 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\Bill; +use FireflyIII\Models\ImportJob; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Rules\IsBase64; +use FireflyIII\Rules\IsValidAttachmentModel; + +/** + * Class AttachmentRequest + */ +class AttachmentRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'filename' => $this->string('filename'), + 'title' => $this->string('title'), + 'notes' => $this->string('notes'), + 'model' => $this->string('model'), + 'model_id' => $this->integer('model_id'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $models = implode( + ',', [ + Bill::class, + ImportJob::class, + TransactionJournal::class, + ] + ); + $model = $this->string('model'); + $rules = [ + 'filename' => 'required|between:1,255', + 'title' => 'between:1,255', + 'notes' => 'between:1,65000', + 'model' => sprintf('required|in:%s', $models), + 'model_id' => ['required', 'numeric', new IsValidAttachmentModel($model)], + ]; + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + unset($rules['model'], $rules['model_id']); + $rules['filename'] = 'between:1,255'; + break; + } + + return $rules; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/AvailableBudgetRequest.php b/app/Api/V1/Requests/AvailableBudgetRequest.php new file mode 100644 index 0000000000..5631f5acc1 --- /dev/null +++ b/app/Api/V1/Requests/AvailableBudgetRequest.php @@ -0,0 +1,69 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +/** + * Class AvailableBudgetRequest + */ +class AvailableBudgetRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'transaction_currency_id' => $this->integer('transaction_currency_id'), + 'amount' => $this->string('amount'), + 'start_date' => $this->date('start_date'), + 'end_date' => $this->date('end_date'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'transaction_currency_id' => 'required|numeric|exists:transaction_currencies,id', + 'amount' => 'required|numeric|more:0', + 'start_date' => 'required|date|before:end_date', + 'end_date' => 'required|date|after:start_date', + ]; + + return $rules; + } + + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/BillRequest.php b/app/Api/V1/Requests/BillRequest.php index 9f68a656cd..f58c977737 100644 --- a/app/Api/V1/Requests/BillRequest.php +++ b/app/Api/V1/Requests/BillRequest.php @@ -86,8 +86,8 @@ class BillRequest extends Request break; case 'PUT': case 'PATCH': - $bill = $this->route()->parameter('bill'); - $rules['name'] .= ',' . $bill->id; + $bill = $this->route()->parameter('bill'); + $rules['name'] .= ',' . $bill->id; break; } diff --git a/app/Api/V1/Requests/BudgetLimitRequest.php b/app/Api/V1/Requests/BudgetLimitRequest.php new file mode 100644 index 0000000000..8ede47cfce --- /dev/null +++ b/app/Api/V1/Requests/BudgetLimitRequest.php @@ -0,0 +1,77 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + + +/** + * Class BudgetLimitRequest + */ +class BudgetLimitRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'budget_id' => $this->integer('budget_id'), + 'start_date' => $this->date('start_date'), + 'end_date' => $this->date('end_date'), + 'amount' => $this->string('amount'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id', + 'start_date' => 'required|before:end_date|date', + 'end_date' => 'required|after:start_date|date', + 'amount' => 'required|more:0', + ]; + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + $rules['budget_id'] = 'required|exists:budgets,id|belongsToUser:budgets,id'; + break; + } + + return $rules; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/BudgetRequest.php b/app/Api/V1/Requests/BudgetRequest.php new file mode 100644 index 0000000000..e90866d505 --- /dev/null +++ b/app/Api/V1/Requests/BudgetRequest.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\Budget; + +/** + * Class BudgetRequest + */ +class BudgetRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'name' => $this->string('name'), + 'active' => $this->boolean('active'), + 'order' => 0, + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name', + 'active' => 'required|boolean', + ]; + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var Budget $budget */ + $budget = $this->route()->parameter('budget'); + $rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id); + break; + } + + return $rules; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/CategoryRequest.php b/app/Api/V1/Requests/CategoryRequest.php new file mode 100644 index 0000000000..b910674e54 --- /dev/null +++ b/app/Api/V1/Requests/CategoryRequest.php @@ -0,0 +1,75 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\Category; + +/** + * Class CategoryRequest + */ +class CategoryRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'name' => $this->string('name'), + 'active' => $this->boolean('active'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|between:1,100|uniqueObjectForUser:categories,name', + 'active' => 'required|boolean', + ]; + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var Category $category */ + $category = $this->route()->parameter('category'); + $rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:categories,name,%d', $category->id); + break; + } + + return $rules; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/CurrencyRequest.php b/app/Api/V1/Requests/CurrencyRequest.php index 8e2edfeda9..6c63c83ddc 100644 --- a/app/Api/V1/Requests/CurrencyRequest.php +++ b/app/Api/V1/Requests/CurrencyRequest.php @@ -41,7 +41,7 @@ class CurrencyRequest extends Request /** * @return array */ - public function getAll() + public function getAll(): array { return [ 'name' => $this->string('name'), diff --git a/app/Api/V1/Requests/JournalLinkRequest.php b/app/Api/V1/Requests/JournalLinkRequest.php new file mode 100644 index 0000000000..e52d5b7484 --- /dev/null +++ b/app/Api/V1/Requests/JournalLinkRequest.php @@ -0,0 +1,68 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + + +/** + * + * Class JournalLinkRequest + */ +class JournalLinkRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'link_type_id' => $this->integer('link_type_id'), + 'inward_id' => $this->integer('inward_id'), + 'outward_id' => $this->integer('outward_id'), + 'notes' => $this->string('notes'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + return [ + 'link_type_id' => 'required|exists:link_types,id', + 'inward_id' => 'required|belongsToUser:transaction_journals,id', + 'outward_id' => 'required|belongsToUser:transaction_journals,id', + 'notes' => 'between:0,65000', + ]; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/LinkTypeRequest.php b/app/Api/V1/Requests/LinkTypeRequest.php new file mode 100644 index 0000000000..c44dbcb145 --- /dev/null +++ b/app/Api/V1/Requests/LinkTypeRequest.php @@ -0,0 +1,86 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\LinkType; +use Illuminate\Validation\Rule; + +/** + * + * Class LinkTypeRequest + */ +class LinkTypeRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'name' => $this->string('name'), + 'outward' => $this->string('outward'), + 'inward' => $this->string('inward'), + ]; + + + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|unique:link_types,name|min:1', + 'outward' => 'required|unique:link_types,outward|min:1|different:inward', + 'inward' => 'required|unique:link_types,inward|min:1|different:outward', + ]; + // Rule::unique('users')->ignore($user->id), + + + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var LinkType $linkType */ + $linkType = $this->route()->parameter('linkType'); + $rules['name'] = ['required', Rule::unique('link_types', 'name')->ignore($linkType->id), 'min:1']; + $rules['outward'] = ['required', 'different:inward', Rule::unique('link_types', 'outward')->ignore($linkType->id), 'min:1']; + $rules['inward'] = ['required', 'different:outward', Rule::unique('link_types', 'inward')->ignore($linkType->id), 'min:1']; + break; + } + + return $rules; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/PiggyBankRequest.php b/app/Api/V1/Requests/PiggyBankRequest.php new file mode 100644 index 0000000000..15a41bda01 --- /dev/null +++ b/app/Api/V1/Requests/PiggyBankRequest.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\PiggyBank; +use FireflyIII\Rules\IsAssetAccountId; + +/** + * + * Class PiggyBankRequest + */ +class PiggyBankRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'name' => $this->string('name'), + 'account_id' => $this->integer('account_id'), + 'targetamount' => $this->string('target_amount'), + 'current_amount' => $this->string('current_amount'), + 'start_date' => $this->date('start_date'), + 'target_date' => $this->date('target_date'), + 'note' => $this->string('notes'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|between:1,255|uniquePiggyBankForUser', + 'account_id' => ['required', 'belongsToUser:accounts', new IsAssetAccountId], + 'target_amount' => 'required|numeric|more:0', + 'current_amount' => 'numeric|more:0|lte:target_amount', + 'start_date' => 'date|nullable', + 'target_date' => 'date|nullable', + 'notes' => 'max:65000', + ]; + + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var PiggyBank $piggyBank */ + $piggyBank = $this->route()->parameter('piggyBank'); + $rules['name'] = 'required|between:1,255|uniquePiggyBankForUser:' . $piggyBank->id; + break; + } + + + return $rules; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/PreferenceRequest.php b/app/Api/V1/Requests/PreferenceRequest.php new file mode 100644 index 0000000000..38c3a39e11 --- /dev/null +++ b/app/Api/V1/Requests/PreferenceRequest.php @@ -0,0 +1,57 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +/** + * + * Class PreferenceRequest + */ +class PreferenceRequest extends Request +{ + + + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + public function getAll(): array + { + return [ + 'data' => $this->get('data'), + ]; + } + + public function rules(): array + { + return [ + 'data' => 'required|between:1,65000', + ]; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/RecurrenceRequest.php b/app/Api/V1/Requests/RecurrenceRequest.php new file mode 100644 index 0000000000..3aa948722a --- /dev/null +++ b/app/Api/V1/Requests/RecurrenceRequest.php @@ -0,0 +1,482 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use Carbon\Carbon; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Rules\BelongsUser; +use Illuminate\Validation\Validator; +use InvalidArgumentException; +use Log; + +/** + * Class RecurrenceRequest + */ +class RecurrenceRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + $return = [ + 'recurrence' => [ + 'type' => $this->string('type'), + 'title' => $this->string('title'), + 'description' => $this->string('description'), + 'first_date' => $this->date('first_date'), + 'repeat_until' => $this->date('repeat_until'), + 'repetitions' => $this->integer('nr_of_repetitions'), + 'apply_rules' => $this->boolean('apply_rules'), + 'active' => $this->boolean('active'), + ], + 'meta' => [ + 'piggy_bank_id' => $this->integer('piggy_bank_id'), + 'piggy_bank_name' => $this->string('piggy_bank_name'), + 'tags' => explode(',', $this->string('tags')), + ], + 'transactions' => [], + 'repetitions' => [], + ]; + + // repetition data: + /** @var array $repetitions */ + $repetitions = $this->get('repetitions'); + /** @var array $repetition */ + foreach ($repetitions as $repetition) { + $return['repetitions'][] = [ + 'type' => $repetition['type'], + 'moment' => $repetition['moment'], + 'skip' => (int)$repetition['skip'], + 'weekend' => (int)$repetition['weekend'], + ]; + } + // transaction data: + /** @var array $transactions */ + $transactions = $this->get('transactions'); + /** @var array $transaction */ + foreach ($transactions as $transaction) { + $return['transactions'][] = [ + 'amount' => $transaction['amount'], + + 'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null, + 'currency_code' => $transaction['currency_code'] ?? null, + + 'foreign_amount' => $transaction['foreign_amount'] ?? null, + 'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null, + 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, + + 'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null, + 'budget_name' => $transaction['budget_name'] ?? null, + 'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null, + 'category_name' => $transaction['category_name'] ?? null, + + 'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null, + 'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null, + 'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null, + 'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null, + + 'description' => $transaction['description'], + ]; + } + + return $return; + } + + /** + * @return array + */ + public function rules(): array + { + $today = new Carbon; + $today->addDay(); + + return [ + 'type' => 'required|in:withdrawal,transfer,deposit', + 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', + 'description' => 'between:1,65000', + 'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')), + 'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')), + 'nr_of_repetitions' => 'numeric|between:1,31', + 'apply_rules' => 'required|boolean', + 'active' => 'required|boolean', + + // rules for meta values: + 'tags' => 'between:1,64000', + 'piggy_bank_id' => 'numeric', + + // rules for repetitions. + 'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly', + 'repetitions.*.moment' => 'between:0,10', + 'repetitions.*.skip' => 'required|numeric|between:0,31', + 'repetitions.*.weekend' => 'required|numeric|min:1|max:4', + + // rules for transactions. + 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code', + 'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id', + 'transactions.*.foreign_amount' => 'numeric|more:0', + 'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id', + 'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code', + 'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser], + 'transactions.*.category_name' => 'between:1,255|nullable', + 'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser], + 'transactions.*.source_name' => 'between:1,255|nullable', + 'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser], + 'transactions.*.destination_name' => 'between:1,255|nullable', + 'transactions.*.amount' => 'required|numeric|more:0', + 'transactions.*.description' => 'required|between:1,255', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + $this->atLeastOneTransaction($validator); + $this->atLeastOneRepetition($validator); + $this->validRepeatsUntil($validator); + $this->validRepetitionMoment($validator); + $this->foreignCurrencyInformation($validator); + $this->validateAccountInformation($validator); + } + ); + } + + /** + * Throws an error when this asset account is invalid. + * + * @noinspection MoreThanThreeArgumentsInspection + * + * @param Validator $validator + * @param int|null $accountId + * @param null|string $accountName + * @param string $idField + * @param string $nameField + * + * @return null|Account + */ + protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account + { + $accountId = (int)$accountId; + $accountName = (string)$accountName; + // both empty? hard exit. + if ($accountId < 1 && '' === $accountName) { + $validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); + + return null; + } + // ID belongs to user and is asset account: + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + $set = $repository->getAccountsById([$accountId]); + Log::debug(sprintf('Count of accounts found by ID %d is: %d', $accountId, $set->count())); + if ($set->count() === 1) { + /** @var Account $first */ + $first = $set->first(); + if ($first->accountType->type !== AccountType::ASSET) { + $validator->errors()->add($idField, trans('validation.belongs_user')); + + return null; + } + + // we ignore the account name at this point. + return $first; + } + + $account = $repository->findByName($accountName, [AccountType::ASSET]); + if (null === $account) { + $validator->errors()->add($nameField, trans('validation.belongs_user')); + + return null; + } + + return $account; + } + + /** + * Adds an error to the validator when there are no repetitions in the array of data. + * + * @param Validator $validator + */ + protected function atLeastOneRepetition(Validator $validator): void + { + $data = $validator->getData(); + $repetitions = $data['repetitions'] ?? []; + // need at least one transaction + if (\count($repetitions) === 0) { + $validator->errors()->add('description', trans('validation.at_least_one_repetition')); + } + } + + /** + * Adds an error to the validator when there are no transactions in the array of data. + * + * @param Validator $validator + */ + protected function atLeastOneTransaction(Validator $validator): void + { + $data = $validator->getData(); + $transactions = $data['transactions'] ?? []; + // need at least one transaction + if (\count($transactions) === 0) { + $validator->errors()->add('description', trans('validation.at_least_one_transaction')); + } + } + + /** + * TODO can be made a rule? + * If the transactions contain foreign amounts, there must also be foreign currency information. + * + * @param Validator $validator + */ + protected function foreignCurrencyInformation(Validator $validator): void + { + $data = $validator->getData(); + $transactions = $data['transactions'] ?? []; + foreach ($transactions as $index => $transaction) { + // must have currency info. + if (isset($transaction['foreign_amount']) + && !(isset($transaction['foreign_currency_id']) + || isset($transaction['foreign_currency_code']))) { + $validator->errors()->add( + 'transactions.' . $index . '.foreign_amount', + trans('validation.require_currency_info') + ); + } + } + } + + /** + * Throws an error when the given opposing account (of type $type) is invalid. + * Empty data is allowed, system will default to cash. + * + * @noinspection MoreThanThreeArgumentsInspection + * + * @param Validator $validator + * @param string $type + * @param int|null $accountId + * @param null|string $accountName + * @param string $idField + * + * @return null|Account + */ + protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account + { + $accountId = (int)$accountId; + $accountName = (string)$accountName; + // both empty? done! + if ($accountId < 1 && \strlen($accountName) === 0) { + return null; + } + if ($accountId !== 0) { + // ID belongs to user and is $type account: + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + $set = $repository->getAccountsById([$accountId]); + if ($set->count() === 1) { + /** @var Account $first */ + $first = $set->first(); + if ($first->accountType->type !== $type) { + $validator->errors()->add($idField, trans('validation.belongs_user')); + + return null; + } + + // we ignore the account name at this point. + return $first; + } + } + + // not having an opposing account by this name is NOT a problem. + return null; + } + + /** + * TODO can be a rule? + * + * Validates the given account information. Switches on given transaction type. + * + * @param Validator $validator + */ + protected function validateAccountInformation(Validator $validator): void + { + $data = $validator->getData(); + $transactions = $data['transactions'] ?? []; + $idField = 'description'; + $transactionType = $data['type'] ?? 'false'; + foreach ($transactions as $index => $transaction) { + $sourceId = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null; + $sourceName = $transaction['source_name'] ?? null; + $destinationId = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null; + $destinationName = $transaction['destination_name'] ?? null; + $sourceAccount = null; + $destinationAccount = null; + switch ($transactionType) { + case 'withdrawal': + $idField = 'transactions.' . $index . '.source_id'; + $nameField = 'transactions.' . $index . '.source_name'; + $sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); + $idField = 'transactions.' . $index . '.destination_id'; + $destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField); + break; + case 'deposit': + $idField = 'transactions.' . $index . '.source_id'; + $sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField); + + $idField = 'transactions.' . $index . '.destination_id'; + $nameField = 'transactions.' . $index . '.destination_name'; + $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); + break; + case 'transfer': + $idField = 'transactions.' . $index . '.source_id'; + $nameField = 'transactions.' . $index . '.source_name'; + $sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField); + + $idField = 'transactions.' . $index . '.destination_id'; + $nameField = 'transactions.' . $index . '.destination_name'; + $destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField); + break; + default: + $validator->errors()->add($idField, trans('validation.invalid_account_info')); + + return; + + } + // add some errors in case of same account submitted: + if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) { + $validator->errors()->add($idField, trans('validation.source_equals_destination')); + } + } + } + + /** + * @param Validator $validator + */ + private function validRepeatsUntil(Validator $validator): void + { + $data = $validator->getData(); + $repetitions = $data['nr_of_repetitions'] ?? null; + $repeatUntil = $data['repeat_until'] ?? null; + if (null !== $repetitions && null !== $repeatUntil) { + // expect a date OR count: + $validator->errors()->add('repeat_until', trans('validation.require_repeat_until')); + $validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until')); + + return; + } + } + + /** + * TODO merge this in a rule somehow. + * + * @param Validator $validator + */ + private function validRepetitionMoment(Validator $validator): void + { + $data = $validator->getData(); + $repetitions = $data['repetitions'] ?? []; + /** + * @var int $index + * @var array $repetition + */ + foreach ($repetitions as $index => $repetition) { + switch ($repetition['type']) { + default: + $validator->errors()->add(sprintf('repetitions.%d.type', $index), trans('validation.valid_recurrence_rep_type')); + + return; + case 'daily': + if ('' !== (string)$repetition['moment']) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + } + + return; + case 'monthly': + $dayOfMonth = (int)$repetition['moment']; + if ($dayOfMonth < 1 || $dayOfMonth > 31) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + } + + return; + case 'ndom': + $parameters = explode(',', $repetition['moment']); + if (\count($parameters) !== 2) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + + return; + } + $nthDay = (int)($parameters[0] ?? 0.0); + $dayOfWeek = (int)($parameters[1] ?? 0.0); + if ($nthDay < 1 || $nthDay > 5) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + + return; + } + if ($dayOfWeek < 1 || $dayOfWeek > 7) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + + return; + } + + return; + case 'weekly': + $dayOfWeek = (int)$repetition['moment']; + if ($dayOfWeek < 1 || $dayOfWeek > 7) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + + return; + } + break; + case 'yearly': + try { + Carbon::createFromFormat('Y-m-d', $repetition['moment']); + } catch (InvalidArgumentException $e) { + $validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment')); + + return; + } + } + } + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/RuleGroupRequest.php b/app/Api/V1/Requests/RuleGroupRequest.php new file mode 100644 index 0000000000..a8b88601e4 --- /dev/null +++ b/app/Api/V1/Requests/RuleGroupRequest.php @@ -0,0 +1,79 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\RuleGroup; + + +/** + * + * Class RuleGroupRequest + */ +class RuleGroupRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'title' => $this->string('title'), + 'description' => $this->string('description'), + 'active' => $this->boolean('active'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'title' => 'required|between:1,100|uniqueObjectForUser:rule_groups,title', + 'description' => 'between:1,5000|nullable', + 'active' => 'required|boolean', + ]; + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var RuleGroup $ruleGroup */ + $ruleGroup = $this->route()->parameter('ruleGroup'); + $rules['title'] = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . $ruleGroup->id; + break; + } + + return $rules; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/RuleRequest.php b/app/Api/V1/Requests/RuleRequest.php new file mode 100644 index 0000000000..84f6afb52b --- /dev/null +++ b/app/Api/V1/Requests/RuleRequest.php @@ -0,0 +1,156 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use Illuminate\Validation\Validator; + + +/** + * Class RuleRequest + */ +class RuleRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + $data = [ + 'title' => $this->string('title'), + 'description' => $this->string('description'), + 'rule_group_id' => $this->integer('rule_group_id'), + 'rule_group_title' => $this->string('rule_group_title'), + 'trigger' => $this->string('trigger'), + 'strict' => $this->boolean('strict'), + 'stop-processing' => $this->boolean('stop_processing'), + 'active' => $this->boolean('active'), + 'rule-triggers' => [], + 'rule-actions' => [], + ]; + + foreach ($this->get('rule-triggers') as $trigger) { + $data['rule-triggers'][] = [ + 'name' => $trigger['name'], + 'value' => $trigger['value'], + 'stop-processing' => (int)($trigger['stop-processing'] ?? 0) === 1, + ]; + } + foreach ($this->get('rule-actions') as $action) { + $data['rule-actions'][] = [ + 'name' => $action['name'], + 'value' => $action['value'], + 'stop-processing' => (int)($action['stop-processing'] ?? 0) === 1, + ]; + } + + return $data; + } + + /** + * @return array + */ + public function rules(): array + { + $validTriggers = array_keys(config('firefly.rule-triggers')); + $validActions = array_keys(config('firefly.rule-actions')); + + // some actions require text: + $contextActions = implode(',', config('firefly.rule-actions-text')); + + $rules = [ + 'title' => 'required|between:1,100|uniqueObjectForUser:rules,title', + 'description' => 'between:1,5000|nullable', + 'rule_group_id' => 'required|belongsToUser:rule_groups|required_without:rule_group_title', + 'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title', + 'trigger' => 'required|in:store-journal,update-journal', + 'rule-triggers.*.name' => 'required|in:' . implode(',', $validTriggers), + 'rule-triggers.*.stop-processing' => 'boolean', + 'rule-triggers.*.value' => 'required|min:1|ruleTriggerValue', // + 'rule-actions.*.name' => 'required|in:' . implode(',', $validActions), + 'rule-actions.*.value' => 'required_if:rule-action.*.type,' . $contextActions . '|ruleActionValue', + 'rule-actions.*.stop-processing' => 'boolean', + 'strict' => 'required|boolean', + 'stop_processing' => 'required|boolean', + 'active' => 'required|boolean', + ]; + + return $rules; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + $this->atLeastOneTrigger($validator); + $this->atLeastOneAction($validator); + } + ); + } + + /** + * Adds an error to the validator when there are no repetitions in the array of data. + * + * @param Validator $validator + */ + protected function atLeastOneAction(Validator $validator): void + { + $data = $validator->getData(); + $repetitions = $data['rule-actions'] ?? []; + // need at least one transaction + if (\count($repetitions) === 0) { + $validator->errors()->add('title', trans('validation.at_least_one_action')); + } + } + + /** + * Adds an error to the validator when there are no repetitions in the array of data. + * + * @param Validator $validator + */ + protected function atLeastOneTrigger(Validator $validator): void + { + $data = $validator->getData(); + $repetitions = $data['rule-triggers'] ?? []; + // need at least one transaction + if (\count($repetitions) === 0) { + $validator->errors()->add('title', trans('validation.at_least_one_trigger')); + } + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/TransactionRequest.php b/app/Api/V1/Requests/TransactionRequest.php index ab03ec561a..ce0c27e1be 100644 --- a/app/Api/V1/Requests/TransactionRequest.php +++ b/app/Api/V1/Requests/TransactionRequest.php @@ -188,6 +188,8 @@ class TransactionRequest extends Request /** * Throws an error when this asset account is invalid. * + * @noinspection MoreThanThreeArgumentsInspection + * * @param Validator $validator * @param int|null $accountId * @param null|string $accountName @@ -256,7 +258,7 @@ class TransactionRequest extends Request * * @param Validator $validator */ - protected function checkValidDescriptions(Validator $validator) + protected function checkValidDescriptions(Validator $validator): void { $data = $validator->getData(); $transactions = $data['transactions'] ?? []; @@ -317,6 +319,8 @@ class TransactionRequest extends Request } /** + * TODO can be made a rule? + * * If the transactions contain foreign amounts, there must also be foreign currency information. * * @param Validator $validator @@ -342,6 +346,8 @@ class TransactionRequest extends Request * Throws an error when the given opposing account (of type $type) is invalid. * Empty data is allowed, system will default to cash. * + * @noinspection MoreThanThreeArgumentsInspection + * * @param Validator $validator * @param string $type * @param int|null $accountId @@ -454,7 +460,7 @@ class TransactionRequest extends Request * * @throws FireflyException */ - protected function validateSplitAccounts(Validator $validator) + protected function validateSplitAccounts(Validator $validator): void { $data = $validator->getData(); $count = isset($data['transactions']) ? \count($data['transactions']) : 0; diff --git a/app/Console/Commands/UpgradeDatabase.php b/app/Console/Commands/UpgradeDatabase.php index c4f31d21d2..a533aedf2f 100644 --- a/app/Console/Commands/UpgradeDatabase.php +++ b/app/Console/Commands/UpgradeDatabase.php @@ -184,7 +184,7 @@ class UpgradeDatabase extends Command ] ); } - if($bill->amount_max === $bill->amount_min) { + if ($bill->amount_max === $bill->amount_min) { RuleTrigger::create( [ 'rule_id' => $rule->id, diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 08ec12f3df..0b3ecc5f4d 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -90,6 +90,7 @@ class VerifyDatabase extends Command $this->createAccessTokens(); $this->fixDoubleAmounts(); $this->fixBadMeta(); + $this->removeBills(); } /** @@ -164,7 +165,8 @@ class VerifyDatabase extends Command if (isset($results[$key]) && $results[$key] !== $category) { $this->error( sprintf( - 'Transaction #%d referred to the wrong category. Was category #%d but is fixed to be category #%d.', $obj->transaction_journal_id, $category, $results[$key] + 'Transaction #%d referred to the wrong category. Was category #%d but is fixed to be category #%d.', $obj->transaction_journal_id, + $category, $results[$key] ) ); DB::table('category_transaction')->where('id', $obj->ct_id)->update(['category_id' => $results[$key]]); @@ -184,14 +186,15 @@ class VerifyDatabase extends Command ->get(['transactions.id', 'transaction_journal_id', 'identifier', 'budget_transaction.budget_id', 'budget_transaction.id as ct_id']); $results = []; foreach ($set as $obj) { - $key = $obj->transaction_journal_id . '-' . $obj->identifier; + $key = $obj->transaction_journal_id . '-' . $obj->identifier; $budget = (int)$obj->budget_id; // value exists and is not budget: if (isset($results[$key]) && $results[$key] !== $budget) { $this->error( sprintf( - 'Transaction #%d referred to the wrong budget. Was budget #%d but is fixed to be budget #%d.', $obj->transaction_journal_id, $budget, $results[$key] + 'Transaction #%d referred to the wrong budget. Was budget #%d but is fixed to be budget #%d.', $obj->transaction_journal_id, $budget, + $results[$key] ) ); DB::table('budget_transaction')->where('id', $obj->ct_id)->update(['budget_id' => $results[$key]]); @@ -253,6 +256,23 @@ class VerifyDatabase extends Command } } + /** + * + */ + private function removeBills(): void + { + /** @var TransactionType $withdrawal */ + $withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); + $journals = TransactionJournal::whereNotNull('bill_id') + ->where('transaction_type_id', '!=', $withdrawal->id)->get(); + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id)); + $journal->bill_id = null; + $journal->save(); + } + } + /** * Eeport (and fix) piggy banks. Make sure there are only transfers linked to piggy bank events. */ diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 3f24caad90..bda9202d93 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -24,11 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Console; +use Carbon\Carbon; +use FireflyIII\Jobs\CreateRecurringTransactions; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; /** - * File to make sure commnds work. + * File to make sure commands work. */ class Kernel extends ConsoleKernel { @@ -44,7 +46,7 @@ class Kernel extends ConsoleKernel /** * Register the commands for the application. */ - protected function commands() + protected function commands(): void { $this->load(__DIR__ . '/Commands'); @@ -55,10 +57,9 @@ class Kernel extends ConsoleKernel * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { + $schedule->job(new CreateRecurringTransactions(new Carbon))->daily(); } } diff --git a/app/Events/RequestedReportOnJournals.php b/app/Events/RequestedReportOnJournals.php new file mode 100644 index 0000000000..d4bdf0dbb7 --- /dev/null +++ b/app/Events/RequestedReportOnJournals.php @@ -0,0 +1,46 @@ +userId = $userId; + $this->journals = $journals; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index d66d42a274..6a72b8ab29 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -132,20 +132,15 @@ class Handler extends ExceptionHandler $doMailError = env('SEND_ERROR_MESSAGE', true); if ( // if the user wants us to mail: - $doMailError === true && - (( + $doMailError === true + && ( // and if is one of these error instances - $exception instanceof FireflyException - || $exception instanceof ErrorException - || $exception instanceof OAuthServerException + $exception instanceof FireflyException + || $exception instanceof ErrorException + || $exception instanceof OAuthServerException - ) - || ( - // or this one, but it's a JSON exception. - $exception instanceof AuthenticationException - && Request::expectsJson() === true - )) - ) { + ) + ) { // then, send email $userData = [ 'id' => 0, diff --git a/app/Export/ExpandedProcessor.php b/app/Export/ExpandedProcessor.php index d8ad004f9f..3c964efeba 100644 --- a/app/Export/ExpandedProcessor.php +++ b/app/Export/ExpandedProcessor.php @@ -36,12 +36,12 @@ use FireflyIII\Models\AccountMeta; use FireflyIII\Models\ExportJob; use FireflyIII\Models\Note; use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use Illuminate\Support\Collection; use Log; use Storage; use ZipArchive; -use FireflyIII\Models\TransactionJournal; /** * Class ExpandedProcessor. diff --git a/app/Factory/AttachmentFactory.php b/app/Factory/AttachmentFactory.php new file mode 100644 index 0000000000..b728625a4c --- /dev/null +++ b/app/Factory/AttachmentFactory.php @@ -0,0 +1,79 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Factory; + +use FireflyIII\Models\Attachment; +use FireflyIII\Models\Note; +use FireflyIII\User; + +/** + * Class AttachmentFactory + */ +class AttachmentFactory +{ + /** @var User */ + private $user; + + /** + * @param array $data + * + * @return Attachment|null + */ + public function create(array $data): ?Attachment + { + // create attachment: + $attachment = Attachment::create( + [ + 'user_id' => $this->user->id, + 'attachable_id' => $data['model_id'], + 'attachable_type' => $data['model'], + 'md5' => '', + 'filename' => $data['filename'], + 'title' => '' === $data['title'] ? null : $data['title'], + 'description' => null, + 'mime' => '', + 'size' => 0, + 'uploaded' => 0, + ] + ); + $notes = (string)($data['notes'] ?? ''); + if ('' !== $notes) { + $note = new Note; + $note->noteable()->associate($attachment); + $note->text = $notes; + $note->save(); + } + + return $attachment; + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + +} \ No newline at end of file diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index 94327af9ee..101d225452 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -58,8 +58,8 @@ class BillFactory 'date' => $data['date'], 'repeat_freq' => $data['repeat_freq'], 'skip' => $data['skip'], - 'automatch' => true, - 'active' => $data['active'], + 'automatch' => $data['automatch'] ?? true, + 'active' => $data['active'] ?? true, ] ); diff --git a/app/Factory/CategoryFactory.php b/app/Factory/CategoryFactory.php index ff80a61a7b..245d8e9eb6 100644 --- a/app/Factory/CategoryFactory.php +++ b/app/Factory/CategoryFactory.php @@ -72,7 +72,7 @@ class CategoryFactory Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName)); - if (\strlen($categoryName) === 0 && $categoryId === 0) { + if ('' === $categoryName && $categoryId === 0) { return null; } // first by ID: diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php new file mode 100644 index 0000000000..f144022ed7 --- /dev/null +++ b/app/Factory/RecurrenceFactory.php @@ -0,0 +1,91 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Factory; + + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; +use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; +use FireflyIII\Services\Internal\Support\TransactionServiceTrait; +use FireflyIII\Services\Internal\Support\TransactionTypeTrait; +use FireflyIII\User; +use Log; + +/** + * Class RecurrenceFactory + */ +class RecurrenceFactory +{ + use TransactionTypeTrait, TransactionServiceTrait, RecurringTransactionTrait; + + /** @var User */ + private $user; + + /** + * @param array $data + * + * @return Recurrence + */ + public function create(array $data): ?Recurrence + { + try { + $type = $this->findTransactionType(ucfirst($data['recurrence']['type'])); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + + return null; + } + $repetitions = (int)$data['recurrence']['repetitions']; + $recurrence = new Recurrence( + [ + 'user_id' => $this->user->id, + 'transaction_type_id' => $type->id, + 'title' => $data['recurrence']['title'], + 'description' => $data['recurrence']['description'], + 'first_date' => $data['recurrence']['first_date']->format('Y-m-d'), + 'repeat_until' => $repetitions > 0 ? null : $data['recurrence']['repeat_until'], + 'latest_date' => null, + 'repetitions' => $data['recurrence']['repetitions'], + 'apply_rules' => $data['recurrence']['apply_rules'], + 'active' => $data['recurrence']['active'], + ] + ); + $recurrence->save(); + + $this->updateMetaData($recurrence, $data); + $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + $this->createTransactions($recurrence, $data['transactions'] ?? []); + + return $recurrence; + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + +} \ No newline at end of file diff --git a/app/Factory/TransactionCurrencyFactory.php b/app/Factory/TransactionCurrencyFactory.php index 3cee423c42..e57e1f81d1 100644 --- a/app/Factory/TransactionCurrencyFactory.php +++ b/app/Factory/TransactionCurrencyFactory.php @@ -71,6 +71,7 @@ class TransactionCurrencyFactory if ('' === $currencyCode && $currencyId === 0) { Log::warning('Cannot find anything on empty currency code and empty currency ID!'); + return null; } diff --git a/app/Factory/TransactionFactory.php b/app/Factory/TransactionFactory.php index c9c787d092..bb7bdc2550 100644 --- a/app/Factory/TransactionFactory.php +++ b/app/Factory/TransactionFactory.php @@ -26,6 +26,7 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -56,6 +57,7 @@ class TransactionFactory $currencyId = $data['currency_id'] ?? null; $currencyId = isset($data['currency']) ? $data['currency']->id : $currencyId; if ('' === $data['amount']) { + Log::error('Empty string in data.', $data); throw new FireflyException('Amount is an empty string, which Firefly III cannot handle. Apologies.'); // @codeCoverageIgnore } if (null === $currencyId) { @@ -96,14 +98,27 @@ class TransactionFactory $description = $journal->description === $data['description'] ? null : $data['description']; // type of source account depends on journal type: - $sourceType = $this->accountType($journal, 'source'); + $sourceType = $this->accountType($journal, 'source'); Log::debug(sprintf('Expect source account to be of type %s', $sourceType)); $sourceAccount = $this->findAccount($sourceType, $data['source_id'], $data['source_name']); // same for destination account: - $destinationType = $this->accountType($journal, 'destination'); + $destinationType = $this->accountType($journal, 'destination'); Log::debug(sprintf('Expect source destination to be of type %s', $destinationType)); $destinationAccount = $this->findAccount($destinationType, $data['destination_id'], $data['destination_name']); + + Log::debug(sprintf('Source type is "%s", destination type is "%s"', $sourceAccount->accountType->type, $destinationAccount->accountType->type)); + // throw big fat error when source type === dest type + if ($sourceAccount->accountType->type === $destinationAccount->accountType->type + && ($journal->transactionType->type !== TransactionType::TRANSFER + && $journal->transactionType->type !== TransactionType::RECONCILIATION) + ) { + throw new FireflyException(sprintf('Source and destination account cannot be both of the type "%s"', $destinationAccount->accountType->type)); + } + if ($sourceAccount->accountType->type !== AccountType::ASSET && $destinationAccount->accountType->type !== AccountType::ASSET) { + throw new FireflyException('At least one of the accounts must be an asset account.'); + } + // first make a "negative" (source) transaction based on the data in the array. $source = $this->create( [ diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index fbee73ea96..e81c50c087 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -26,8 +26,8 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\TransactionType; use FireflyIII\Services\Internal\Support\JournalServiceTrait; +use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; use Log; @@ -36,7 +36,7 @@ use Log; */ class TransactionJournalFactory { - use JournalServiceTrait; + use JournalServiceTrait, TransactionTypeTrait; /** @var User */ private $user; @@ -53,7 +53,7 @@ class TransactionJournalFactory $type = $this->findTransactionType($data['type']); $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); Log::debug(sprintf('Going to store a %s', $type->type)); - $journal = TransactionJournal::create( + $journal = TransactionJournal::create( [ 'user_id' => $data['user'], 'transaction_type_id' => $type->id, @@ -67,6 +67,10 @@ class TransactionJournalFactory ] ); + if (isset($data['transactions'][0]['amount']) && '' === $data['transactions'][0]['amount']) { + Log::error('Empty amount in data', $data); + } + // store basic transactions: /** @var TransactionFactory $factory */ $factory = app(TransactionFactory::class); @@ -95,13 +99,17 @@ class TransactionJournalFactory // store date meta fields (if present): $fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date', - 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash','importHashV2', 'external_id']; + 'due_date', 'recurrence_id', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash', 'importHashV2', + 'external_id']; foreach ($fields as $field) { $this->storeMeta($journal, $data, $field); } Log::debug('End of TransactionJournalFactory::create()'); + // invalidate cache. + app('preferences')->mark(); + return $journal; } @@ -133,25 +141,4 @@ class TransactionJournalFactory } } - /** - * Get the transaction type. Since this is mandatory, will throw an exception when nothing comes up. Will always - * use TransactionType repository. - * - * @param string $type - * - * @return TransactionType - * @throws FireflyException - */ - protected function findTransactionType(string $type): TransactionType - { - $factory = app(TransactionTypeFactory::class); - $transactionType = $factory->find($type); - if (null === $transactionType) { - Log::error(sprintf('Could not find transaction type for "%s"', $type)); // @codeCoverageIgnore - throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); // @codeCoverageIgnore - } - - return $transactionType; - } - } diff --git a/app/Generator/Chart/Basic/ChartJsGenerator.php b/app/Generator/Chart/Basic/ChartJsGenerator.php index da6252548f..ef66a2b7cf 100644 --- a/app/Generator/Chart/Basic/ChartJsGenerator.php +++ b/app/Generator/Chart/Basic/ChartJsGenerator.php @@ -90,7 +90,9 @@ class ChartJsGenerator implements GeneratorInterface if (isset($set['currency_symbol'])) { $currentSet['currency_symbol'] = $set['currency_symbol']; } - + if(isset($set['backgroundColor'])) { + $currentSet['backgroundColor'] = $set['backgroundColor']; + } $chartData['datasets'][] = $currentSet; } diff --git a/app/Handlers/Events/APIEventHandler.php b/app/Handlers/Events/APIEventHandler.php index 64aa286420..e73b151e86 100644 --- a/app/Handlers/Events/APIEventHandler.php +++ b/app/Handlers/Events/APIEventHandler.php @@ -28,7 +28,6 @@ use Exception; use FireflyIII\Mail\AccessTokenCreatedMail; use FireflyIII\Repositories\User\UserRepositoryInterface; use Laravel\Passport\Events\AccessTokenCreated; -use Laravel\Passport\Token; use Log; use Mail; use Request; diff --git a/app/Handlers/Events/AutomationHandler.php b/app/Handlers/Events/AutomationHandler.php new file mode 100644 index 0000000000..3585e0ccf2 --- /dev/null +++ b/app/Handlers/Events/AutomationHandler.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Handlers\Events; + +use Exception; +use FireflyIII\Events\RequestedReportOnJournals; +use FireflyIII\Mail\ReportNewJournalsMail; +use FireflyIII\Repositories\User\UserRepositoryInterface; +use Log; +use Mail; + +/** + * Class AutomationHandler + */ +class AutomationHandler +{ + + /** + * @param RequestedReportOnJournals $event + * + * @return bool + */ + public function reportJournals(RequestedReportOnJournals $event): bool + { + Log::debug('In reportJournals.'); + /** @var UserRepositoryInterface $repository */ + $repository = app(UserRepositoryInterface::class); + $user = $repository->findNull($event->userId); + if (null === $user) { + Log::debug('User is NULL'); + + return true; + } + if ($event->journals->count() === 0) { + Log::debug('No journals.'); + + return true; + } + + try { + Log::debug('Trying to mail...'); + Mail::to($user->email)->send(new ReportNewJournalsMail($user->email, '127.0.0.1', $event->journals)); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + Log::error($e->getMessage()); + } + Log::debug('Done!'); + + // @codeCoverageIgnoreEnd + return true; + } +} \ No newline at end of file diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index e953e039d1..64ef4a83fc 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -108,6 +108,27 @@ class UserEventHandler return true; } + /** + * @param Login $event + * + * @return bool + */ + public function demoUserBackToEnglish(Login $event): bool + { + /** @var UserRepositoryInterface $repository */ + $repository = app(UserRepositoryInterface::class); + + /** @var User $user */ + $user = $event->user; + if ($repository->hasRole($user, 'demo')) { + // set user back to English. + app('preferences')->setForUser($user, 'language', 'en_US'); + app('preferences')->mark(); + } + + return true; + } + /** * @param UserChangedEmail $event * diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index 2cecd97947..2b7b9a1f0f 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -41,7 +41,7 @@ class VersionCheckEventHandler /** * @param RequestedVersionCheckStatus $event */ - public function checkForUpdates(RequestedVersionCheckStatus $event) + public function checkForUpdates(RequestedVersionCheckStatus $event): void { // in Sandstorm, cannot check for updates: $sandstorm = 1 === (int)getenv('SANDSTORM'); diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 72a00bab0c..b6d950042f 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -122,6 +122,46 @@ class AttachmentHelper implements AttachmentHelperInterface return $this->messages; } + /** + * Uploads a file as a string. + * + * @param Attachment $attachment + * @param string $content + * + * @return bool + */ + public function saveAttachmentFromApi(Attachment $attachment, string $content): bool + { + $resource = tmpfile(); + if (false === $resource) { + Log::error('Cannot create temp-file for file upload.'); + + return false; + } + $path = stream_get_meta_data($resource)['uri']; + fwrite($resource, $content); + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $mime = finfo_file($finfo, $path); + $allowedMime = config('firefly.allowedMimes'); + if (!\in_array($mime, $allowedMime, true)) { + Log::error(sprintf('Mime type %s is not allowed for API file upload.', $mime)); + + return false; + } + // is allowed? Save the file! + $encrypted = Crypt::encrypt($content); + $this->uploadDisk->put($attachment->fileName(), $encrypted); + + // update attachment. + $attachment->md5 = md5_file($path); + $attachment->mime = $mime; + $attachment->size = \strlen($content); + $attachment->uploaded = 1; + $attachment->save(); + + return true; + } + /** * @param Model $model * @param array|null $files @@ -232,7 +272,7 @@ class AttachmentHelper implements AttachmentHelperInterface Log::debug(sprintf('Name is %s, and mime is %s', $name, $mime)); Log::debug('Valid mimes are', $this->allowedMimes); - if (!\in_array($mime, $this->allowedMimes)) { + if (!\in_array($mime, $this->allowedMimes, true)) { $msg = (string)trans('validation.file_invalid_mime', ['name' => $name, 'mime' => $mime]); $this->errors->add('attachments', $msg); Log::error($msg); diff --git a/app/Helpers/Attachments/AttachmentHelperInterface.php b/app/Helpers/Attachments/AttachmentHelperInterface.php index 276d312a27..8f34041faa 100644 --- a/app/Helpers/Attachments/AttachmentHelperInterface.php +++ b/app/Helpers/Attachments/AttachmentHelperInterface.php @@ -37,14 +37,14 @@ interface AttachmentHelperInterface * * @return string */ - public function getAttachmentLocation(Attachment $attachment): string; + public function getAttachmentContent(Attachment $attachment): string; /** * @param Attachment $attachment * * @return string */ - public function getAttachmentContent(Attachment $attachment): string; + public function getAttachmentLocation(Attachment $attachment): string; /** * @return Collection @@ -61,6 +61,16 @@ interface AttachmentHelperInterface */ public function getMessages(): MessageBag; + /** + * Uploads a file as a string. + * + * @param Attachment $attachment + * @param string $content + * + * @return bool + */ + public function saveAttachmentFromApi(Attachment $attachment, string $content): bool; + /** * @param Model $model * @param null|array $files diff --git a/app/Helpers/Collection/BalanceLine.php b/app/Helpers/Collection/BalanceLine.php index a998b4784e..9d62a4aa21 100644 --- a/app/Helpers/Collection/BalanceLine.php +++ b/app/Helpers/Collection/BalanceLine.php @@ -40,10 +40,6 @@ class BalanceLine * */ public const ROLE_TAGROLE = 2; - /** - * - */ - public const ROLE_DIFFROLE = 3; /** @var Collection */ protected $balanceEntries; @@ -167,9 +163,6 @@ class BalanceLine if (self::ROLE_TAGROLE === $this->getRole()) { return (string)trans('firefly.coveredWithTags'); } - if (self::ROLE_DIFFROLE === $this->getRole()) { - return (string)trans('firefly.leftUnbalanced'); - } return ''; } diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index b2eb90dc1a..33d24fcaf7 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -321,6 +321,14 @@ class JournalCollector implements JournalCollectorInterface return $journals; } + /** + * @return EloquentBuilder + */ + public function getQuery(): EloquentBuilder + { + return $this->query; + } + /** * @return JournalCollectorInterface */ diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index b710326175..b9c29ff4bf 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -27,6 +27,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; use FireflyIII\User; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -35,6 +36,7 @@ use Illuminate\Support\Collection; */ interface JournalCollectorInterface { + /** * @param string $filter * @@ -78,6 +80,11 @@ interface JournalCollectorInterface */ public function getPaginatedJournals(): LengthAwarePaginator; + /** + * @return EloquentBuilder + */ + public function getQuery(): EloquentBuilder; + /** * @return JournalCollectorInterface */ diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index 7c9358e4bb..a9fcd1869a 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -24,9 +24,10 @@ namespace FireflyIII\Helpers\Help; use Cache; use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use League\CommonMark\CommonMarkConverter; use Log; -use Requests; use Route; /** @@ -64,20 +65,21 @@ class Help implements HelpInterface { $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); Log::debug(sprintf('Trying to get %s...', $uri)); - $opt = ['useragent' => $this->userAgent]; + $opt = ['headers' => ['User-Agent' => $this->userAgent]]; $content = ''; try { - $result = Requests::get($uri, [], $opt); - } catch (Exception $e) { + $client = new Client; + $res = $client->request('GET', $uri, $opt); + } catch (GuzzleException|Exception $e) { Log::error($e); return ''; } - Log::debug(sprintf('Status code is %d', $result->status_code)); + Log::debug(sprintf('Status code is %d', $res->getStatusCode())); - if (200 === $result->status_code) { - $content = trim($result->body); + if (200 === $res->getStatusCode()) { + $content = trim($res->getBody()->getContents()); } if (\strlen($content) > 0) { diff --git a/app/Helpers/Report/PopupReport.php b/app/Helpers/Report/PopupReport.php index 73e786e24b..24c1bd2d9e 100644 --- a/app/Helpers/Report/PopupReport.php +++ b/app/Helpers/Report/PopupReport.php @@ -36,33 +36,6 @@ use Illuminate\Support\Collection; */ class PopupReport implements PopupReportInterface { - /** - * @param $account - * @param $attributes - * - * @return Collection - */ - public function balanceDifference($account, $attributes): Collection - { - // row that displays difference - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class); - $collector - ->setAccounts(new Collection([$account])) - ->setTypes([TransactionType::WITHDRAWAL]) - ->setRange($attributes['startDate'], $attributes['endDate']) - ->withoutBudget(); - $journals = $collector->getJournals(); - - return $journals->filter( - function (Transaction $transaction) { - $tags = $transaction->transactionJournal->tags()->where('tagMode', 'balancingAct')->count(); - - return 0 === $tags; - } - ); - } - /** * @param Budget $budget * @param Account $account diff --git a/app/Helpers/Report/PopupReportInterface.php b/app/Helpers/Report/PopupReportInterface.php index 69efef80e6..6345afdcc3 100644 --- a/app/Helpers/Report/PopupReportInterface.php +++ b/app/Helpers/Report/PopupReportInterface.php @@ -32,14 +32,6 @@ use Illuminate\Support\Collection; */ interface PopupReportInterface { - /** - * @param $account - * @param $attributes - * - * @return Collection - */ - public function balanceDifference($account, $attributes): Collection; - /** * @param Budget $budget * @param Account $account diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index db68a5c841..225e52b89a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -34,7 +34,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; @@ -52,8 +51,6 @@ class AccountController extends Controller { /** @var CurrencyRepositoryInterface */ private $currencyRepos; - /** @var JournalRepositoryInterface */ - private $journalRepos; /** @var AccountRepositoryInterface */ private $repository; @@ -72,7 +69,6 @@ class AccountController extends Controller $this->repository = app(AccountRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); - $this->journalRepos = app(JournalRepositoryInterface::class); return $next($request); } @@ -188,7 +184,9 @@ class AccountController extends Controller $currency = $default; } - $preFilled = [ + // code to handle active-checkboxes + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ 'accountNumber' => $repository->getMetaValue($account, 'accountNumber'), 'accountRole' => $repository->getMetaValue($account, 'accountRole'), 'ccType' => $repository->getMetaValue($account, 'ccType'), @@ -199,7 +197,7 @@ class AccountController extends Controller 'virtualBalance' => $account->virtual_balance, 'currency_id' => $currency->id, 'notes' => '', - 'active' => $account->active, + 'active' => $hasOldInput ? (bool)$request->old('active') : $account->active, ]; /** @var Note $note */ $note = $this->repository->getNote($account); @@ -207,7 +205,6 @@ class AccountController extends Controller $preFilled['notes'] = $note->text; } - $request->session()->flash('preFilled', $preFilled); return view('accounts.edit', compact('account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles', 'preFilled')); diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 92d0760119..b53ac0b274 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -51,8 +51,9 @@ class HomeController extends Controller { $title = (string)trans('firefly.administration'); $mainTitleIcon = 'fa-hand-spock-o'; + $sandstorm = 1 === (int)getenv('SANDSTORM'); - return view('admin.index', compact('title', 'mainTitleIcon')); + return view('admin.index', compact('title', 'mainTitleIcon', 'sandstorm')); } /** diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index be3be8fdb4..5884596fa0 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -112,7 +112,7 @@ class LinkController extends Controller public function destroy(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType) { $name = $linkType->name; - $moveTo = $repository->find((int)$request->get('move_link_type_before_delete')); + $moveTo = $repository->findNull((int)$request->get('move_link_type_before_delete')); $repository->destroy($linkType, $moveTo); $request->session()->flash('success', (string)trans('firefly.deleted_link_type', ['name' => $name])); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index bbb5a96234..b0ae134570 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -161,10 +161,13 @@ class BillController extends Controller $bill->amount_max = round($bill->amount_max, $currency->decimal_places); $defaultCurrency = app('amount')->getDefaultCurrency(); + // code to handle active-checkboxes + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ 'notes' => $this->billRepository->getNoteText($bill), 'transaction_currency_id' => $bill->transaction_currency_id, - 'active' => $bill->active, + 'active' => $hasOldInput ? (bool)$request->old('active') : $bill->active, ]; $request->session()->flash('preFilled', $preFilled); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index ed199483d5..df6f0d8954 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -96,7 +96,7 @@ class BudgetController extends Controller // if today is between start and end, use the diff in days between end and today (days left) // otherwise, use diff between start and end. $today = new Carbon; - Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'),$today->format('Y-m-d'))); + Log::debug(sprintf('Start is %s, end is %s, today is %s', $start->format('Y-m-d'), $end->format('Y-m-d'), $today->format('Y-m-d'))); if ($today->gte($start) && $today->lte($end)) { $days = $end->diffInDays($today); $daysInMonth = $start->diffInDays($today); @@ -216,11 +216,18 @@ class BudgetController extends Controller { $subTitle = trans('firefly.edit_budget', ['name' => $budget->name]); + // code to handle active-checkboxes + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ + 'active' => $hasOldInput ? (bool)$request->old('active') : $budget->active, + ]; + // put previous url in session if not redirect from store (not "return_to_edit"). if (true !== session('budgets.edit.fromUpdate')) { $this->rememberPreviousUri('budgets.edit.uri'); } $request->session()->forget('budgets.edit.fromUpdate'); + $request->session()->flash('preFilled', $preFilled); return view('budgets.edit', compact('budget', 'subTitle')); } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 30c5657d59..dfc8f77e1d 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -27,6 +27,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; +use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; use Log; use Steam; @@ -57,9 +58,9 @@ class ReportController extends Controller * @param Carbon $start * @param Carbon $end * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ - public function netWorth(Collection $accounts, Carbon $start, Carbon $end) + public function netWorth(Collection $accounts, Carbon $start, Carbon $end): JsonResponse { // chart properties for cache: $cache = new CacheProperties; @@ -111,14 +112,16 @@ class ReportController extends Controller $source = $this->getChartData($accounts, $start, $end); $chartData = [ [ - 'label' => trans('firefly.income'), - 'type' => 'bar', - 'entries' => [], + 'label' => trans('firefly.income'), + 'type' => 'bar', + 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green + 'entries' => [], ], [ - 'label' => trans('firefly.expenses'), - 'type' => 'bar', - 'entries' => [], + 'label' => trans('firefly.expenses'), + 'type' => 'bar', + 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red + 'entries' => [], ], ]; @@ -188,17 +191,19 @@ class ReportController extends Controller $chartData = [ [ - 'label' => (string)trans('firefly.income'), - 'type' => 'bar', - 'entries' => [ + 'label' => (string)trans('firefly.income'), + 'type' => 'bar', + 'backgroundColor' => 'rgba(0, 141, 76, 0.5)', // green + 'entries' => [ (string)trans('firefly.sum_of_period') => $numbers['sum_earned'], (string)trans('firefly.average_in_period') => $numbers['avg_earned'], ], ], [ - 'label' => trans('firefly.expenses'), - 'type' => 'bar', - 'entries' => [ + 'label' => trans('firefly.expenses'), + 'type' => 'bar', + 'backgroundColor' => 'rgba(219, 68, 55, 0.5)', // red + 'entries' => [ (string)trans('firefly.sum_of_period') => $numbers['sum_spent'], (string)trans('firefly.average_in_period') => $numbers['avg_spent'], ], diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index 11f52acf5c..1caf4925df 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -23,13 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; +use Artisan; use Carbon\Carbon; use DB; use Exception; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Middleware\IsDemoUser; use Illuminate\Http\Request; +use Illuminate\Routing\Route; use Log; use Monolog\Handler\RotatingFileHandler; +use Preferences; +use Route as RouteFacade; /** * Class DebugController @@ -45,6 +50,52 @@ class DebugController extends Controller $this->middleware(IsDemoUser::class); } + /** + * @throws FireflyException + */ + public function displayError() + { + Log::debug('This is a test message at the DEBUG level.'); + Log::info('This is a test message at the INFO level.'); + Log::notice('This is a test message at the NOTICE level.'); + Log::warning('This is a test message at the WARNING level.'); + Log::error('This is a test message at the ERROR level.'); + Log::critical('This is a test message at the CRITICAL level.'); + Log::alert('This is a test message at the ALERT level.'); + Log::emergency('This is a test message at the EMERGENCY level.'); + throw new FireflyException('A very simple test error.'); + } + + /** + * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function flush(Request $request) + { + Preferences::mark(); + $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range']); + Log::debug('Call cache:clear...'); + Artisan::call('cache:clear'); + Log::debug('Call config:clear...'); + Artisan::call('config:clear'); + Log::debug('Call route:clear...'); + Artisan::call('route:clear'); + Log::debug('Call twig:clean...'); + try { + Artisan::call('twig:clean'); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + // don't care + Log::debug('Called twig:clean.'); + } + // @codeCoverageIgnoreEnd + Log::debug('Call view:clear...'); + Artisan::call('view:clear'); + Log::debug('Done! Redirecting...'); + + return redirect(route('index')); + } /** * @param Request $request @@ -111,13 +162,70 @@ class DebugController extends Controller return view( 'debug', compact( - 'phpVersion', 'extensions', 'localeAttempts', 'appEnv', 'appDebug', 'appLog', 'appLogLevel', 'now', 'packages', 'drivers', 'currentDriver', - 'userAgent', 'displayErrors', 'errorReporting', 'phpOs', 'interface', 'logContent', 'cacheDriver', 'isDocker', 'isSandstorm', 'trustedProxies', - 'toSandbox' - ) + 'phpVersion', 'extensions', 'localeAttempts', 'appEnv', 'appDebug', 'appLog', 'appLogLevel', 'now', 'packages', 'drivers', + 'currentDriver', + 'userAgent', 'displayErrors', 'errorReporting', 'phpOs', 'interface', 'logContent', 'cacheDriver', 'isDocker', 'isSandstorm', + 'trustedProxies', + 'toSandbox' + ) ); } + /** + * @return string + */ + public function routes(): string + { + $set = RouteFacade::getRoutes(); + $ignore = ['chart.', 'javascript.', 'json.', 'report-data.', 'popup.', 'debugbar.', 'attachments.download', 'attachments.preview', + 'bills.rescan', 'budgets.income', 'currencies.def', 'error', 'flush', 'help.show', 'import.file', + 'login', 'logout', 'password.reset', 'profile.confirm-email-change', 'profile.undo-email-change', + 'register', 'report.options', 'routes', 'rule-groups.down', 'rule-groups.up', 'rules.up', 'rules.down', + 'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch', + 'two-factor.lost', 'reports.options', 'debug', 'import.create-job', 'import.download', 'import.start', 'import.status.json', + 'preferences.delete-code', 'rules.test-triggers', 'piggy-banks.remove-money', 'piggy-banks.add-money', + 'accounts.reconcile.transactions', 'accounts.reconcile.overview', 'export.download', + 'transactions.clone', 'two-factor.index', 'api.v1', 'installer.', 'attachments.view', 'import.create', + 'import.job.download', 'import.job.start', 'import.job.status.json', 'import.job.store', 'recurring.events', + 'recurring.suggest', + ]; + $return = ' '; + /** @var Route $route */ + foreach ($set as $route) { + $name = $route->getName(); + if (null !== $name && \strlen($name) > 0 && \in_array('GET', $route->methods(), true)) { + + $found = false; + foreach ($ignore as $string) { + if (!(false === stripos($name, $string))) { + $found = true; + break; + } + } + if ($found === false) { + $return .= 'touch ' . $route->getName() . '.md;'; + } + } + } + + return $return; + } + + /** + * @param Request $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function testFlash(Request $request) + { + $request->session()->flash('success', 'This is a success message.'); + $request->session()->flash('info', 'This is an info message.'); + $request->session()->flash('warning', 'This is a warning.'); + $request->session()->flash('error', 'This is an error!'); + + return redirect(route('home')); + } + /** * Some common combinations. * @@ -149,7 +257,7 @@ class DebugController extends Controller private function collectPackages(): array { $packages = []; - $file = realpath(__DIR__ . '/../../../vendor/composer/installed.json'); + $file = \dirname(__DIR__, 3) . '/vendor/composer/installed.json'; if (!($file === false) && file_exists($file)) { // file exists! $content = file_get_contents($file); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 993d3273e5..6dc45a198d 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -22,11 +22,8 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; -use Artisan; use Carbon\Carbon; -use Exception; use FireflyIII\Events\RequestedVersionCheckStatus; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Middleware\Installer; use FireflyIII\Http\Middleware\IsDemoUser; @@ -35,11 +32,9 @@ 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 View; /** @@ -99,53 +94,6 @@ class HomeController extends Controller } - /** - * @throws FireflyException - */ - public function displayError() - { - Log::debug('This is a test message at the DEBUG level.'); - Log::info('This is a test message at the INFO level.'); - Log::notice('This is a test message at the NOTICE level.'); - Log::warning('This is a test message at the WARNING level.'); - Log::error('This is a test message at the ERROR level.'); - Log::critical('This is a test message at the CRITICAL level.'); - Log::alert('This is a test message at the ALERT level.'); - Log::emergency('This is a test message at the EMERGENCY level.'); - throw new FireflyException('A very simple test error.'); - } - - /** - * @param Request $request - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function flush(Request $request) - { - Preferences::mark(); - $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range']); - Log::debug('Call cache:clear...'); - Artisan::call('cache:clear'); - Log::debug('Call config:clear...'); - Artisan::call('config:clear'); - Log::debug('Call route:clear...'); - Artisan::call('route:clear'); - Log::debug('Call twig:clean...'); - try { - Artisan::call('twig:clean'); - // @codeCoverageIgnoreStart - } catch (Exception $e) { - // don't care - Log::debug('Called twig:clean.'); - } - // @codeCoverageIgnoreEnd - Log::debug('Call view:clear...'); - Artisan::call('view:clear'); - Log::debug('Done! Redirecting...'); - - return redirect(route('index')); - } - /** * @param AccountRepositoryInterface $repository * @@ -193,56 +141,4 @@ class HomeController extends Controller ); } - /** - * @return string - */ - public function routes() - { - $set = RouteFacade::getRoutes(); - $ignore = ['chart.', 'javascript.', 'json.', 'report-data.', 'popup.', 'debugbar.', 'attachments.download', 'attachments.preview', - 'bills.rescan', 'budgets.income', 'currencies.def', 'error', 'flush', 'help.show', 'import.file', - 'login', 'logout', 'password.reset', 'profile.confirm-email-change', 'profile.undo-email-change', - 'register', 'report.options', 'routes', 'rule-groups.down', 'rule-groups.up', 'rules.up', 'rules.down', - 'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch', - 'two-factor.lost', 'reports.options', 'debug', 'import.create-job', 'import.download', 'import.start', 'import.status.json', - 'preferences.delete-code', 'rules.test-triggers', 'piggy-banks.remove-money', 'piggy-banks.add-money', - 'accounts.reconcile.transactions', 'accounts.reconcile.overview', 'export.download', - 'transactions.clone', 'two-factor.index', - ]; - $return = ' '; - /** @var Route $route */ - foreach ($set as $route) { - $name = $route->getName(); - if (null !== $name && \in_array('GET', $route->methods()) && \strlen($name) > 0) { - - $found = false; - foreach ($ignore as $string) { - if (!(false === stripos($name, $string))) { - $found = true; - break; - } - } - if ($found === false) { - $return .= 'touch ' . $route->getName() . '.md;'; - } - } - } - - return $return; - } - - /** - * @param Request $request - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector - */ - public function testFlash(Request $request) - { - $request->session()->flash('success', 'This is a success message.'); - $request->session()->flash('info', 'This is an info message.'); - $request->session()->flash('warning', 'This is a warning.'); - $request->session()->flash('error', 'This is an error!'); - - return redirect(route('home')); - } } diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index a6fe9d3167..72c422caad 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -161,10 +161,8 @@ class IndexController extends Controller $config['delimiter'] = $config['delimiter'] ?? ','; $config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter']; - // this prevents private information from escaping - $config['column-mapping-config'] = []; - $result = json_encode($config, JSON_PRETTY_PRINT); - $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); + $result = json_encode($config, JSON_PRETTY_PRINT); + $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); /** @var LaravelResponse $response */ $response = response($result, 200); $response->header('Content-disposition', 'attachment; filename=' . $name) diff --git a/app/Http/Controllers/Import/JobStatusController.php b/app/Http/Controllers/Import/JobStatusController.php index d512e82196..8a9ce1d851 100644 --- a/app/Http/Controllers/Import/JobStatusController.php +++ b/app/Http/Controllers/Import/JobStatusController.php @@ -127,7 +127,7 @@ class JobStatusController extends Controller public function start(ImportJob $importJob): JsonResponse { // catch impossible status: - $allowed = ['ready_to_run', 'need_job_config', 'error']; // todo remove error + $allowed = ['ready_to_run', 'need_job_config']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error('Job is not ready.'); diff --git a/app/Http/Controllers/Json/BoxController.php b/app/Http/Controllers/Json/BoxController.php index 27c857d97b..e61c522daf 100644 --- a/app/Http/Controllers/Json/BoxController.php +++ b/app/Http/Controllers/Json/BoxController.php @@ -153,6 +153,12 @@ class BoxController extends Controller $incomes[$currencyId] = Amount::formatAnything($currency, $incomes[$currencyId] ?? '0', false); $expenses[$currencyId] = Amount::formatAnything($currency, $expenses[$currencyId] ?? '0', false); } + if (\count($sums) === 0) { + $currency = app('amount')->getDefaultCurrency(); + $sums[$currency->id] = Amount::formatAnything($currency, '0', false); + $incomes[$currency->id] = Amount::formatAnything($currency, '0', false); + $expenses[$currency->id] = Amount::formatAnything($currency, '0', false); + } $response = [ 'incomes' => $incomes, @@ -161,6 +167,7 @@ class BoxController extends Controller 'size' => \count($sums), ]; + $cache->store($response); return response()->json($response); diff --git a/app/Http/Controllers/Json/ExchangeController.php b/app/Http/Controllers/Json/ExchangeController.php index e4846f43f7..34b8eeddd6 100644 --- a/app/Http/Controllers/Json/ExchangeController.php +++ b/app/Http/Controllers/Json/ExchangeController.php @@ -50,7 +50,7 @@ class ExchangeController extends Controller $rate = $repository->getExchangeRate($fromCurrency, $toCurrency, $date); - if (null === $rate->id) { + if (null === $rate) { Log::debug(sprintf('No cached exchange rate in database for %s to %s on %s', $fromCurrency->code, $toCurrency->code, $date->format('Y-m-d'))); // create service: diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index ed57022b60..0f30b1c9d7 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -127,6 +127,7 @@ class ReportController extends Controller $budget = $this->budgetRepository->findNull((int)$attributes['budgetId']); $account = $this->accountRepository->findNull((int)$attributes['accountId']); + switch (true) { case BalanceLine::ROLE_DEFAULTROLE === $role && null !== $budget->id: // normal row with a budget: @@ -137,10 +138,6 @@ class ReportController extends Controller $journals = $this->popupHelper->balanceForNoBudget($account, $attributes); $budget->name = (string)trans('firefly.no_budget'); break; - case BalanceLine::ROLE_DIFFROLE === $role: - $journals = $this->popupHelper->balanceDifference($account, $attributes); - $budget->name = (string)trans('firefly.leftUnbalanced'); - break; case BalanceLine::ROLE_TAGROLE === $role: // row with tag info. throw new FireflyException('Firefly cannot handle this type of info-button (BalanceLine::TagRole)'); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 581f88b35d..fc02ea3c55 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -107,9 +107,10 @@ class ProfileController extends Controller $domain = $this->getDomain(); $secret = Google2FA::generateSecretKey(); session()->flash('two-factor-secret', $secret); + $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret, 200); - return view('profile.code', compact('image')); + return view('profile.code', compact('image', 'secret')); } /** diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php new file mode 100644 index 0000000000..817f6f73dd --- /dev/null +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -0,0 +1,142 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Recurring; + + +use Carbon\Carbon; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Requests\RecurrenceFormRequest; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use Illuminate\Http\Request; + +/** + * + * Class CreateController + */ +class CreateController extends Controller +{ + /** @var BudgetRepositoryInterface */ + private $budgets; + /** @var RecurringRepositoryInterface */ + private $recurring; + + /** + * + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', trans('firefly.recurrences')); + app('view')->share('subTitle', trans('firefly.create_new_recurrence')); + + $this->recurring = app(RecurringRepositoryInterface::class); + $this->budgets = app(BudgetRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function create(Request $request) + { + $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets()); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $tomorrow = new Carbon; + $oldRepetitionType = $request->old('repetition_type'); + $tomorrow->addDay(); + + // put previous url in session if not redirect from store (not "create another"). + if (true !== session('recurring.create.fromStore')) { + $this->rememberPreviousUri('recurring.create.uri'); + } + $request->session()->forget('recurring.create.fromStore'); + + // when will it end? + $repetitionEnds = [ + 'forever' => trans('firefly.repeat_forever'), + 'until_date' => trans('firefly.repeat_until_date'), + 'times' => trans('firefly.repeat_times'), + ]; + // what to do in the weekend? + $weekendResponses = [ + RecurrenceRepetition::WEEKEND_DO_NOTHING => trans('firefly.do_nothing'), + RecurrenceRepetition::WEEKEND_SKIP_CREATION => trans('firefly.skip_transaction'), + RecurrenceRepetition::WEEKEND_TO_FRIDAY => trans('firefly.jump_to_friday'), + RecurrenceRepetition::WEEKEND_TO_MONDAY => trans('firefly.jump_to_monday'), + ]; + + // flash some data: + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ + 'first_date' => $tomorrow->format('Y-m-d'), + 'transaction_type' => $hasOldInput ? $request->old('transaction_type') : 'withdrawal', + 'active' => $hasOldInput ? (bool)$request->old('active') : true, + 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : true, + ]; + $request->session()->flash('preFilled', $preFilled); + + return view( + 'recurring.create', compact('tomorrow', 'oldRepetitionType', 'weekendResponses', 'preFilled', 'repetitionEnds', 'defaultCurrency', 'budgets') + ); + } + + /** + * @param RecurrenceFormRequest $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function store(RecurrenceFormRequest $request) + { + $data = $request->getAll(); + $recurrence = $this->recurring->store($data); + + $request->session()->flash('success', (string)trans('firefly.stored_new_recurrence', ['title' => $recurrence->title])); + app('preferences')->mark(); + + if (1 === (int)$request->get('create_another')) { + // set value so create routine will not overwrite URL: + $request->session()->put('recurring.create.fromStore', true); + + return redirect(route('recurring.create'))->withInput(); + } + + // redirect to previous URL. + return redirect($this->getPreviousUri('recurring.create.uri')); + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Recurring/DeleteController.php b/app/Http/Controllers/Recurring/DeleteController.php new file mode 100644 index 0000000000..a36a0ce018 --- /dev/null +++ b/app/Http/Controllers/Recurring/DeleteController.php @@ -0,0 +1,93 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Recurring; + + +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use Illuminate\Http\Request; + +/** + * Class DeleteController + */ +class DeleteController extends Controller +{ + /** @var RecurringRepositoryInterface */ + private $recurring; + + /** + * + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', trans('firefly.recurrences')); + + $this->recurring = app(RecurringRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Recurrence $recurrence + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function delete(Recurrence $recurrence) + { + $subTitle = trans('firefly.delete_recurring', ['title' => $recurrence->title]); + // put previous url in session + $this->rememberPreviousUri('recurrences.delete.uri'); + + // todo actual number. + $journalsCreated = $this->recurring->getTransactions($recurrence)->count(); + + return view('recurring.delete', compact('recurrence', 'subTitle', 'journalsCreated')); + } + + /** + * @param RecurringRepositoryInterface $repository + * @param Request $request + * @param Recurrence $recurrence + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function destroy(RecurringRepositoryInterface $repository, Request $request, Recurrence $recurrence) + { + $repository->destroy($recurrence); + $request->session()->flash('success', (string)trans('firefly.' . 'recurrence_deleted', ['title' => $recurrence->title])); + app('preferences')->mark(); + + return redirect($this->getPreviousUri('recurrences.delete.uri')); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php new file mode 100644 index 0000000000..e78b8a820f --- /dev/null +++ b/app/Http/Controllers/Recurring/EditController.php @@ -0,0 +1,169 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Recurring; + + +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Requests\RecurrenceFormRequest; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use Illuminate\Http\Request; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * + * Class EditController + */ +class EditController extends Controller +{ + /** @var BudgetRepositoryInterface */ + private $budgets; + /** @var RecurringRepositoryInterface */ + private $recurring; + + /** + * + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', trans('firefly.recurrences')); + app('view')->share('subTitle', trans('firefly.recurrences')); + + $this->recurring = app(RecurringRepositoryInterface::class); + $this->budgets = app(BudgetRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Request $request + * @param Recurrence $recurrence + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function edit(Request $request, Recurrence $recurrence) + { + + + // use transformer: + $transformer = new RecurrenceTransformer(new ParameterBag); + $array = $transformer->transform($recurrence); + $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets()); + + // get recurrence type: + // todo move to repository + // todo handle old repetition type as well. + + + /** @var RecurrenceRepetition $repetition */ + $repetition = $recurrence->recurrenceRepetitions()->first(); + $currentRepetitionType = $repetition->repetition_type; + if ('' !== $repetition->repetition_moment) { + $currentRepetitionType .= ',' . $repetition->repetition_moment; + } + + // put previous url in session if not redirect from store (not "return_to_edit"). + if (true !== session('recurrences.edit.fromUpdate')) { + $this->rememberPreviousUri('recurrences.edit.uri'); + } + $request->session()->forget('recurrences.edit.fromUpdate'); + + // assume repeats forever: + $repetitionEnd = 'forever'; + // types of repetitions: + $repetitionEnds = [ + 'forever' => trans('firefly.repeat_forever'), + 'until_date' => trans('firefly.repeat_until_date'), + 'times' => trans('firefly.repeat_times'), + ]; + if (null !== $recurrence->repeat_until) { + $repetitionEnd = 'until_date'; + } + if ($recurrence->repetitions > 0) { + $repetitionEnd = 'times'; + } + + // what to do in the weekend? + $weekendResponses = [ + RecurrenceRepetition::WEEKEND_DO_NOTHING => trans('firefly.do_nothing'), + RecurrenceRepetition::WEEKEND_SKIP_CREATION => trans('firefly.skip_transaction'), + RecurrenceRepetition::WEEKEND_TO_FRIDAY => trans('firefly.jump_to_friday'), + RecurrenceRepetition::WEEKEND_TO_MONDAY => trans('firefly.jump_to_monday'), + ]; + + // code to handle active-checkboxes + $hasOldInput = null !== $request->old('_token'); + // $hasOldInput = false; + $preFilled = [ + 'transaction_type' => strtolower($recurrence->transactionType->type), + 'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active, + 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules, + ]; + + return view( + 'recurring.edit', + compact('recurrence', 'array', 'weekendResponses', 'budgets', 'preFilled', 'currentRepetitionType', 'repetitionEnd', 'repetitionEnds') + ); + } + + /** + * @param RecurrenceFormRequest $request + * @param Recurrence $recurrence + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function update(RecurrenceFormRequest $request, Recurrence $recurrence) + { + $data = $request->getAll(); + $this->recurring->update($recurrence, $data); + + $request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title])); + app('preferences')->mark(); + + if (1 === (int)$request->get('return_to_edit')) { + // set value so edit routine will not overwrite URL: + $request->session()->put('recurrences.edit.fromUpdate', true); + + return redirect(route('recurring.edit', [$recurrence->id]))->withInput(['return_to_edit' => 1]); + } + + // redirect to previous URL. + return redirect($this->getPreviousUri('recurrences.edit.uri')); + } + + +} \ No newline at end of file diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php new file mode 100644 index 0000000000..d7f02b5071 --- /dev/null +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -0,0 +1,240 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Recurring; + + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * + * Class IndexController + */ +class IndexController extends Controller +{ + /** @var RecurringRepositoryInterface */ + private $recurring; + + /** + * + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', trans('firefly.recurrences')); + + $this->recurring = app(RecurringRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Request $request + * + * @throws FireflyException + * @return JsonResponse + */ + function events(Request $request): JsonResponse + { + $return = []; + $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); + $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); + $firstDate = Carbon::createFromFormat('Y-m-d', $request->get('first_date')); + $endDate = '' !== (string)$request->get('end_date') ? Carbon::createFromFormat('Y-m-d', $request->get('end_date')) : null; + $endsAt = (string)$request->get('ends'); + $repetitionType = explode(',', $request->get('type'))[0]; + $repetitions = (int)$request->get('reps'); + $repetitionMoment = ''; + $start->startOfDay(); + + // if $firstDate is beyond $end, simply return an empty array. + if ($firstDate->gt($end)) { + return response()->json([]); + } + // if $firstDate is beyond start, use that one: + $actualStart = clone $firstDate; + + switch ($repetitionType) { + default: + throw new FireflyException(sprintf('Cannot handle repetition type "%s"', $repetitionType)); + case 'daily': + break; + case 'weekly': + case 'monthly': + $repetitionMoment = explode(',', $request->get('type'))[1] ?? '1'; + break; + case 'ndom': + $repetitionMoment = str_ireplace('ndom,', '', $request->get('type')); + break; + case 'yearly': + $repetitionMoment = explode(',', $request->get('type'))[1] ?? '2018-01-01'; + break; + } + $repetition = new RecurrenceRepetition; + $repetition->repetition_type = $repetitionType; + $repetition->repetition_moment = $repetitionMoment; + $repetition->repetition_skip = (int)$request->get('skip'); + $repetition->weekend = (int)$request->get('weekend'); + + $actualEnd = clone $end; + switch ($endsAt) { + default: + throw new FireflyException(sprintf('Cannot generate events for type that ends at "%s".', $endsAt)); + case 'forever': + // simply generate up until $end. No change from default behavior. + $occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd); + break; + case 'until_date': + $actualEnd = $endDate ?? clone $end; + $occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd); + break; + case 'times': + $occurrences = $this->recurring->getXOccurrences($repetition, $actualStart, $repetitions); + break; + } + + + /** @var Carbon $current */ + foreach ($occurrences as $current) { + if ($current->gte($start)) { + $event = [ + 'id' => $repetitionType . $firstDate->format('Ymd'), + 'title' => 'X', + 'allDay' => true, + 'start' => $current->format('Y-m-d'), + 'end' => $current->format('Y-m-d'), + 'editable' => false, + 'rendering' => 'background', + ]; + $return[] = $event; + } + } + + return response()->json($return); + } + + + /** + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function index(Request $request) + { + $page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page'); + $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; + $collection = $this->recurring->get(); + + // TODO: split collection into pages + + $transformer = new RecurrenceTransformer(new ParameterBag); + $recurring = []; + /** @var Recurrence $recurrence */ + foreach ($collection as $recurrence) { + $array = $transformer->transform($recurrence); + $array['first_date'] = new Carbon($array['first_date']); + $array['repeat_until'] = null === $array['repeat_until'] ? null : new Carbon($array['repeat_until']); + $array['latest_date'] = null === $array['latest_date'] ? null : new Carbon($array['latest_date']); + $recurring[] = $array; + } + + return view('recurring.index', compact('recurring', 'page', 'pageSize')); + } + + /** + * @param Request $request + * @param Recurrence $recurrence + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws FireflyException + */ + public function show(Request $request, Recurrence $recurrence) + { + $transformer = new RecurrenceTransformer(new ParameterBag); + $array = $transformer->transform($recurrence); + $page = (int)$request->get('page'); + $pageSize = (int)app('preferences')->get('listPageSize', 50)->data; + $transactions = $this->recurring->getTransactions($recurrence, $page, $pageSize); + + // transform dates back to Carbon objects: + foreach ($array['recurrence_repetitions'] as $index => $repetition) { + foreach ($repetition['occurrences'] as $item => $occurrence) { + $array['recurrence_repetitions'][$index]['occurrences'][$item] = new Carbon($occurrence); + } + } + + $subTitle = trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); + + return view('recurring.show', compact('recurrence', 'subTitle', 'array','transactions')); + } + + /** + * @param Request $request + * + * @return JsonResponse + */ + public function suggest(Request $request): JsonResponse + { + $today = new Carbon; + $date = Carbon::createFromFormat('Y-m-d', $request->get('date')); + $preSelected = (string)$request->get('pre_select'); + $result = []; + if ($date > $today || (string)$request->get('past') === 'true') { + $weekly = sprintf('weekly,%s', $date->dayOfWeekIso); + $monthly = sprintf('monthly,%s', $date->day); + $dayOfWeek = trans(sprintf('config.dow_%s', $date->dayOfWeekIso)); + $ndom = sprintf('ndom,%s,%s', $date->weekOfMonth, $date->dayOfWeekIso); + $yearly = sprintf('yearly,%s', $date->format('Y-m-d')); + $yearlyDate = $date->formatLocalized(trans('config.month_and_day_no_year')); + $result = [ + 'daily' => ['label' => trans('firefly.recurring_daily'), 'selected' => 0 === strpos($preSelected, 'daily')], + $weekly => ['label' => trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]), 'selected' => 0 === strpos($preSelected, 'weekly')], + $monthly => ['label' => trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]), 'selected' => 0 === strpos($preSelected, 'monthly')], + $ndom => ['label' => trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $date->weekOfMonth]), + 'selected' => 0 === strpos($preSelected, 'ndom')], + $yearly => ['label' => trans('firefly.recurring_yearly', ['date' => $yearlyDate]), 'selected' => 0 === strpos($preSelected, 'yearly')], + ]; + } + + + return response()->json($result); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 98a781017c..4ec76ebee9 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -23,13 +23,11 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; -use ExpandedForm; use FireflyIII\Exceptions\FireflyException; 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\Bill; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; @@ -231,6 +229,14 @@ class RuleController extends Controller $actionCount = \count($oldActions); } + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ + 'active' => $hasOldInput ? (bool)$request->old('active') : $rule->active, + 'stop_processing' => $hasOldInput ? (bool)$request->old('stop_processing') : $rule->stop_processing, + 'strict' => $hasOldInput ? (bool)$request->old('strict') : $rule->strict, + + ]; + // get rule trigger for update / store-journal: $primaryTrigger = $this->ruleRepos->getPrimaryTrigger($rule); $subTitle = trans('firefly.edit_rule', ['title' => $rule->title]); @@ -241,6 +247,8 @@ class RuleController extends Controller } session()->forget('rules.edit.fromUpdate'); + $request->session()->flash('preFilled', $preFilled); + return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); } @@ -331,11 +339,11 @@ class RuleController extends Controller public function selectTransactions(Rule $rule) { // does the user have shared accounts? - $first = session('first')->format('Y-m-d'); - $today = Carbon::create()->format('Y-m-d'); - $subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]); + $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( 'first', 'today', 'rule', 'subTitle')); + return view('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle')); } /** @@ -429,6 +437,7 @@ class RuleController extends Controller Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); Log::error($exception->getTraceAsString()); } + // @codeCoverageIgnoreEnd return response()->json(['html' => $view, 'warning' => $warning]); @@ -490,6 +499,7 @@ class RuleController extends Controller Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage())); Log::error($exception->getTraceAsString()); } + // @codeCoverageIgnoreEnd return response()->json(['html' => $view, 'warning' => $warning]); @@ -539,23 +549,39 @@ class RuleController extends Controller { if (0 === $this->ruleRepos->count()) { $data = [ - 'rule_group_id' => $this->ruleRepos->getFirstRuleGroup()->id, - 'stop_processing' => 0, - 'title' => trans('firefly.default_rule_name'), - 'description' => trans('firefly.default_rule_description'), - 'trigger' => 'store-journal', - 'strict' => true, - 'rule-trigger-values' => [ - trans('firefly.default_rule_trigger_description'), - trans('firefly.default_rule_trigger_from_account'), - ], - 'rule-action-values' => [ - trans('firefly.default_rule_action_prepend'), - trans('firefly.default_rule_action_set_category'), - ], + 'rule_group_id' => $this->ruleRepos->getFirstRuleGroup()->id, + 'stop-processing' => 0, + 'title' => trans('firefly.default_rule_name'), + 'description' => trans('firefly.default_rule_description'), + 'trigger' => 'store-journal', + 'strict' => true, + 'rule-triggers' => [ + [ + 'name' => 'description_is', + 'value' => trans('firefly.default_rule_trigger_description'), + 'stop-processing' => false, - 'rule-triggers' => ['description_is', 'from_account_is'], - 'rule-actions' => ['prepend_description', 'set_category'], + ], + [ + 'name' => 'from_account_is', + 'value' => trans('firefly.default_rule_trigger_from_account'), + 'stop-processing' => false, + + ], + + ], + 'rule-actions' => [ + [ + 'name' => 'prepend_description', + 'value' => trans('firefly.default_rule_action_prepend'), + 'stop-processing' => false, + ], + [ + 'name' => 'set_category', + 'value' => trans('firefly.default_rule_action_set_category'), + 'stop-processing' => false, + ], + ], ]; $this->ruleRepos->store($data); @@ -600,6 +626,7 @@ class RuleController extends Controller Log::error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); Log::error($e->getTraceAsString()); } + // @codeCoverageIgnoreEnd return $actions; @@ -809,6 +836,7 @@ class RuleController extends Controller Log::debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); Log::debug($e->getTraceAsString()); } + // @codeCoverageIgnoreEnd return $triggers; diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 360f73eb82..27d3005ee5 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -121,16 +121,18 @@ class RuleGroupController extends Controller } /** + * @param Request $request * @param RuleGroup $ruleGroup * - * @return View + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ - public function edit(RuleGroup $ruleGroup) + public function edit(Request $request, RuleGroup $ruleGroup) { $subTitle = trans('firefly.edit_rule_group', ['title' => $ruleGroup->title]); - $preFilled = [ - 'active' => $ruleGroup->active, + $hasOldInput = null !== $request->old('_token'); + $preFilled = [ + 'active' => $hasOldInput ? (bool)$request->old('active') : $ruleGroup->active, ]; diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index b1dcce8f4e..aeac8035ea 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -87,7 +87,7 @@ class BulkController extends Controller /** - * @param BulkEditJournalRequest $request + * @param BulkEditJournalRequest $request * * @return mixed */ diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 1b09ccc760..6a4b644244 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -30,6 +30,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Http\Request; +use Log; use View; /** @@ -70,6 +71,8 @@ class ConvertController extends Controller { // @codeCoverageIgnoreStart if ($this->isOpeningBalance($journal)) { + Log::debug('This is an opening balance.'); + return $this->redirectToAccount($journal); } // @codeCoverageIgnoreEnd @@ -80,6 +83,7 @@ class ConvertController extends Controller // cannot convert to its own type. if ($sourceType->type === $destinationType->type) { + Log::debug('This is already a transaction of the expected type..'); session()->flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$journal->id])); @@ -87,6 +91,7 @@ class ConvertController extends Controller // cannot convert split. if ($journal->transactions()->count() > 2) { + Log::info('This journal has more than two transactions.'); session()->flash('error', trans('firefly.cannot_convert_split_journal')); return redirect(route('transactions.show', [$journal->id])); @@ -98,8 +103,9 @@ class ConvertController extends Controller return view( 'transactions.convert', compact( - 'sourceType', 'destinationType', 'journal', 'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType', 'subTitle', 'subTitleIcon' - ) + 'sourceType', 'destinationType', 'journal', 'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType', + 'subTitle', 'subTitleIcon' + ) ); } @@ -117,6 +123,7 @@ class ConvertController extends Controller { // @codeCoverageIgnoreStart if ($this->isOpeningBalance($journal)) { + Log::debug('Journal is opening balance, return to account.'); return $this->redirectToAccount($journal); } // @codeCoverageIgnoreEnd @@ -124,12 +131,14 @@ class ConvertController extends Controller $data = $request->all(); if ($journal->transactionType->type === $destinationType->type) { + Log::info('Journal is already of the desired type.'); session()->flash('error', trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$journal->id])); } if ($journal->transactions()->count() > 2) { + Log::info('Journal has more than two transactions.'); session()->flash('error', trans('firefly.cannot_convert_split_journal')); return redirect(route('transactions.show', [$journal->id])); diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 8b78a9ccb9..cd08cf2435 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -24,7 +24,6 @@ namespace FireflyIII\Http\Controllers\Transaction; use Carbon\Carbon; use FireflyIII\Helpers\Collector\JournalCollectorInterface; -use FireflyIII\Helpers\Filter\NegativeAmountFilter; use FireflyIII\Helpers\Filter\TransactionViewFilter; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\MassDeleteJournalRequest; @@ -37,10 +36,9 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Transformers\TransactionTransformer; use Illuminate\Support\Collection; +use Illuminate\View\View as IlluminateView; use Preferences; use Symfony\Component\HttpFoundation\ParameterBag; -use View; -use Illuminate\View\View as IlluminateView; /** * Class MassController. @@ -155,10 +153,11 @@ class MassController extends Controller // transform to array $transactions = $collection->map( function (Transaction $transaction) use ($transformer) { - $transaction= $transformer->transform($transaction); + $transaction = $transformer->transform($transaction); // make sure amount is positive: - $transaction['amount'] = app('steam')->positive((string)$transaction['amount']); + $transaction['amount'] = app('steam')->positive((string)$transaction['amount']); $transaction['foreign_amount'] = app('steam')->positive((string)$transaction['foreign_amount']); + return $transaction; } ); @@ -182,11 +181,11 @@ class MassController extends Controller if (null !== $journal) { // get optional fields: $what = strtolower($this->repository->getTransactionType($journal)); - $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? null; + $sourceAccountId = $request->get('source_id')[$journal->id] ?? null; $currencyId = $request->get('transaction_currency_id')[$journal->id] ?? 1; - $sourceAccountName = $request->get('source_account_name')[$journal->id] ?? null; - $destAccountId = $request->get('destination_account_id')[$journal->id] ?? null; - $destAccountName = $request->get('destination_account_name')[$journal->id] ?? null; + $sourceAccountName = $request->get('source_name')[$journal->id] ?? null; + $destAccountId = $request->get('destination_id')[$journal->id] ?? null; + $destAccountName = $request->get('destination_name')[$journal->id] ?? null; $budgetId = (int)($request->get('budget_id')[$journal->id] ?? 0.0); $category = $request->get('category')[$journal->id]; $tags = $journal->tags->pluck('tag')->toArray(); diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 0f65bf03ff..17da1e6fb5 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -34,9 +34,8 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Log; use Preferences; @@ -49,14 +48,8 @@ class SingleController extends Controller { /** @var AttachmentHelperInterface */ private $attachments; - /** @var BudgetRepositoryInterface */ private $budgets; - /** @var CurrencyRepositoryInterface */ - private $currency; - /** @var PiggyBankRepositoryInterface */ - private $piggyBanks; - /** @var JournalRepositoryInterface */ private $repository; @@ -76,9 +69,7 @@ class SingleController extends Controller $this->middleware( function ($request, $next) { $this->budgets = app(BudgetRepositoryInterface::class); - $this->piggyBanks = app(PiggyBankRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); - $this->currency = app(CurrencyRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class); app('view')->share('title', trans('firefly.transactions')); @@ -100,8 +91,7 @@ class SingleController extends Controller $destination = $this->repository->getJournalDestinationAccounts($journal)->first(); $budgetId = $this->repository->getJournalBudgetId($journal); $categoryName = $this->repository->getJournalCategoryName($journal); - - $tags = implode(',', $this->repository->getTags($journal)); + $tags = implode(',', $this->repository->getTags($journal)); /** @var Transaction $transaction */ $transaction = $journal->transactions()->first(); $amount = app('steam')->positive($transaction->amount); @@ -109,10 +99,10 @@ class SingleController extends Controller $preFilled = [ 'description' => $journal->description, - 'source_account_id' => $source->id, - 'source_account_name' => $source->name, - 'destination_account_id' => $destination->id, - 'destination_account_name' => $destination->name, + 'source_id' => $source->id, + 'source_name' => $source->name, + 'destination_id' => $destination->id, + 'destination_name' => $destination->name, 'amount' => $amount, 'source_amount' => $amount, 'destination_amount' => $foreignAmount, @@ -155,19 +145,21 @@ class SingleController extends Controller $what = strtolower($what); $what = $request->old('what') ?? $what; $budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets()); - $piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount(); - $piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks); $preFilled = session()->has('preFilled') ? session('preFilled') : []; $subTitle = trans('form.add_new_' . $what); $subTitleIcon = 'fa-plus'; $optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data; $source = (int)$request->get('source'); + // grab old currency ID from old data: + $currencyID = (int)$request->old('amount_currency_id_amount'); + $preFilled['amount_currency_id_amount'] = $currencyID; + if (($what === 'withdrawal' || $what === 'transfer') && $source > 0) { - $preFilled['source_account_id'] = $source; + $preFilled['source_id'] = $source; } if ($what === 'deposit' && $source > 0) { - $preFilled['destination_account_id'] = $source; + $preFilled['destination_id'] = $source; } session()->put('preFilled', $preFilled); @@ -178,11 +170,9 @@ class SingleController extends Controller } session()->forget('transactions.create.fromStore'); - asort($piggies); - return view( 'transactions.single.create', - compact('subTitleIcon', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields', 'preFilled') + compact('subTitleIcon', 'budgets', 'what', 'subTitle', 'optionalFields', 'preFilled') ); } @@ -218,7 +208,7 @@ class SingleController extends Controller * * @internal param JournalRepositoryInterface $repository */ - public function destroy(TransactionJournal $transactionJournal) + public function destroy(TransactionJournal $transactionJournal): RedirectResponse { // @codeCoverageIgnoreStart if ($this->isOpeningBalance($transactionJournal)) { @@ -273,35 +263,35 @@ class SingleController extends Controller $pTransaction = $repository->getFirstPosTransaction($journal); $foreignCurrency = $pTransaction->foreignCurrency ?? $pTransaction->transactionCurrency; $preFilled = [ - 'date' => $repository->getJournalDate($journal, null), // $journal->dateAsString() - 'interest_date' => $repository->getJournalDate($journal, 'interest_date'), - 'book_date' => $repository->getJournalDate($journal, 'book_date'), - 'process_date' => $repository->getJournalDate($journal, 'process_date'), - 'category' => $repository->getJournalCategoryName($journal), - 'budget_id' => $repository->getJournalBudgetId($journal), - 'tags' => implode(',', $repository->getTags($journal)), - 'source_account_id' => $sourceAccounts->first()->id, - 'source_account_name' => $sourceAccounts->first()->edit_name, - 'destination_account_id' => $destinationAccounts->first()->id, - 'destination_account_name' => $destinationAccounts->first()->edit_name, + 'date' => $repository->getJournalDate($journal, null), // $journal->dateAsString() + 'interest_date' => $repository->getJournalDate($journal, 'interest_date'), + 'book_date' => $repository->getJournalDate($journal, 'book_date'), + 'process_date' => $repository->getJournalDate($journal, 'process_date'), + 'category' => $repository->getJournalCategoryName($journal), + 'budget_id' => $repository->getJournalBudgetId($journal), + 'tags' => implode(',', $repository->getTags($journal)), + 'source_id' => $sourceAccounts->first()->id, + 'source_name' => $sourceAccounts->first()->edit_name, + 'destination_id' => $destinationAccounts->first()->id, + 'destination_name' => $destinationAccounts->first()->edit_name, // new custom fields: - 'due_date' => $repository->getJournalDate($journal, 'due_date'), - 'payment_date' => $repository->getJournalDate($journal, 'payment_date'), - 'invoice_date' => $repository->getJournalDate($journal, 'invoice_date'), - 'interal_reference' => $repository->getMetaField($journal, 'internal_reference'), - 'notes' => $repository->getNoteText($journal), + 'due_date' => $repository->getJournalDate($journal, 'due_date'), + 'payment_date' => $repository->getJournalDate($journal, 'payment_date'), + 'invoice_date' => $repository->getJournalDate($journal, 'invoice_date'), + 'interal_reference' => $repository->getMetaField($journal, 'internal_reference'), + 'notes' => $repository->getNoteText($journal), // amount fields - 'amount' => $pTransaction->amount, - 'source_amount' => $pTransaction->amount, - 'native_amount' => $pTransaction->amount, - 'destination_amount' => $pTransaction->foreign_amount, - 'currency' => $pTransaction->transactionCurrency, - 'source_currency' => $pTransaction->transactionCurrency, - 'native_currency' => $pTransaction->transactionCurrency, - 'foreign_currency' => $foreignCurrency, - 'destination_currency' => $foreignCurrency, + 'amount' => $pTransaction->amount, + 'source_amount' => $pTransaction->amount, + 'native_amount' => $pTransaction->amount, + 'destination_amount' => $pTransaction->foreign_amount, + 'currency' => $pTransaction->transactionCurrency, + 'source_currency' => $pTransaction->transactionCurrency, + 'native_currency' => $pTransaction->transactionCurrency, + 'foreign_currency' => $foreignCurrency, + 'destination_currency' => $foreignCurrency, ]; // amounts for withdrawals and deposits: @@ -329,9 +319,10 @@ class SingleController extends Controller * @param JournalFormRequest $request * @param JournalRepositoryInterface $repository * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse + * @throws \FireflyIII\Exceptions\FireflyException */ - public function store(JournalFormRequest $request, JournalRepositoryInterface $repository) + public function store(JournalFormRequest $request, JournalRepositoryInterface $repository): RedirectResponse { $doSplit = 1 === (int)$request->get('split_journal'); $createAnother = 1 === (int)$request->get('create_another'); diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index aa25f47fdc..a0b0014576 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -157,7 +157,7 @@ class SplitController extends Controller $type = strtolower($this->repository->getTransactionType($journal)); session()->flash('success', (string)trans('firefly.updated_' . $type, ['description' => $journal->description])); - Preferences::mark(); + app('preferences')->mark(); // @codeCoverageIgnoreStart if (1 === (int)$request->get('return_to_edit')) { @@ -184,30 +184,30 @@ class SplitController extends Controller $sourceAccounts = $this->repository->getJournalSourceAccounts($journal); $destinationAccounts = $this->repository->getJournalDestinationAccounts($journal); $array = [ - 'journal_description' => $request->old('journal_description', $journal->description), - 'journal_amount' => '0', - 'journal_foreign_amount' => '0', - 'sourceAccounts' => $sourceAccounts, - 'journal_source_account_id' => $request->old('journal_source_account_id', $sourceAccounts->first()->id), - 'journal_source_account_name' => $request->old('journal_source_account_name', $sourceAccounts->first()->name), - 'journal_destination_account_id' => $request->old('journal_destination_account_id', $destinationAccounts->first()->id), - 'destinationAccounts' => $destinationAccounts, - 'what' => strtolower($this->repository->getTransactionType($journal)), - 'date' => $request->old('date', $this->repository->getJournalDate($journal, null)), - 'tags' => implode(',', $journal->tags->pluck('tag')->toArray()), + 'journal_description' => $request->old('journal_description', $journal->description), + 'journal_amount' => '0', + 'journal_foreign_amount' => '0', + 'sourceAccounts' => $sourceAccounts, + 'journal_source_id' => $request->old('journal_source_id', $sourceAccounts->first()->id), + 'journal_source_name' => $request->old('journal_source_name', $sourceAccounts->first()->name), + 'journal_destination_id' => $request->old('journal_destination_id', $destinationAccounts->first()->id), + 'destinationAccounts' => $destinationAccounts, + 'what' => strtolower($this->repository->getTransactionType($journal)), + 'date' => $request->old('date', $this->repository->getJournalDate($journal, null)), + 'tags' => implode(',', $journal->tags->pluck('tag')->toArray()), // all custom fields: - 'interest_date' => $request->old('interest_date', $this->repository->getMetaField($journal, 'interest_date')), - 'book_date' => $request->old('book_date', $this->repository->getMetaField($journal, 'book_date')), - 'process_date' => $request->old('process_date', $this->repository->getMetaField($journal, 'process_date')), - 'due_date' => $request->old('due_date', $this->repository->getMetaField($journal, 'due_date')), - 'payment_date' => $request->old('payment_date', $this->repository->getMetaField($journal, 'payment_date')), - 'invoice_date' => $request->old('invoice_date', $this->repository->getMetaField($journal, 'invoice_date')), - 'internal_reference' => $request->old('internal_reference', $this->repository->getMetaField($journal, 'internal_reference')), - 'notes' => $request->old('notes', $this->repository->getNoteText($journal)), + 'interest_date' => $request->old('interest_date', $this->repository->getMetaField($journal, 'interest_date')), + 'book_date' => $request->old('book_date', $this->repository->getMetaField($journal, 'book_date')), + 'process_date' => $request->old('process_date', $this->repository->getMetaField($journal, 'process_date')), + 'due_date' => $request->old('due_date', $this->repository->getMetaField($journal, 'due_date')), + 'payment_date' => $request->old('payment_date', $this->repository->getMetaField($journal, 'payment_date')), + 'invoice_date' => $request->old('invoice_date', $this->repository->getMetaField($journal, 'invoice_date')), + 'internal_reference' => $request->old('internal_reference', $this->repository->getMetaField($journal, 'internal_reference')), + 'notes' => $request->old('notes', $this->repository->getNoteText($journal)), // transactions. - 'transactions' => $this->getTransactionDataFromJournal($journal), + 'transactions' => $this->getTransactionDataFromJournal($journal), ]; // update transactions array with old request data. $array['transactions'] = $this->updateWithPrevious($array['transactions'], $request->old()); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 95cd7d7464..7ad5d34f99 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Transformers\TransactionTransformer; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Log; @@ -96,6 +97,9 @@ class TransactionController extends Controller if ($end < $start) { [$start, $end] = [$end, $start]; } + + $path = route('transactions.index', [$what, $start->format('Y-m-d'), $end->format('Y-m-d')]); + $startStr = $start->formatLocalized($this->monthAndDayFormat); $endStr = $end->formatLocalized($this->monthAndDayFormat); $subTitle = trans('firefly.title_' . $what . '_between', ['start' => $startStr, 'end' => $endStr]); @@ -150,9 +154,9 @@ class TransactionController extends Controller /** * @param Request $request * - * @return \Illuminate\Http\JsonResponse + * @return JsonResponse */ - public function reconcile(Request $request) + public function reconcile(Request $request): JsonResponse { $transactionIds = $request->get('transactions'); foreach ($transactionIds as $transactionId) { diff --git a/app/Http/Requests/BillFormRequest.php b/app/Http/Requests/BillFormRequest.php index 7b0b54083f..dc2d35cf94 100644 --- a/app/Http/Requests/BillFormRequest.php +++ b/app/Http/Requests/BillFormRequest.php @@ -49,15 +49,15 @@ class BillFormRequest extends Request 'date' => $this->date('date'), 'repeat_freq' => $this->string('repeat_freq'), 'skip' => $this->integer('skip'), - 'active' => $this->boolean('active'), 'notes' => $this->string('notes'), + 'active' => $this->boolean('active'), ]; } /** * @return array */ - public function rules() + public function rules(): array { $nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name'; if ($this->integer('id') > 0) { @@ -73,8 +73,7 @@ class BillFormRequest extends Request 'date' => 'required|date', 'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly', 'skip' => 'required|between:0,31', - 'automatch' => 'in:1', - 'active' => 'in:1', + 'active' => 'boolean', ]; return $rules; diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index eaa39797eb..24e450fbb1 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -24,6 +24,8 @@ namespace FireflyIII\Http\Requests; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionType; +use Illuminate\Validation\Validator; +use Log; /** * Class JournalFormRequest. @@ -81,10 +83,10 @@ class JournalFormRequest extends Request 'budget_name' => null, 'category_id' => null, 'category_name' => $this->string('category'), - 'source_id' => $this->integer('source_account_id'), - 'source_name' => $this->string('source_account_name'), - 'destination_id' => $this->integer('destination_account_id'), - 'destination_name' => $this->string('destination_account_name'), + 'source_id' => $this->integer('source_id'), + 'source_name' => $this->string('source_name'), + 'destination_id' => $this->integer('destination_id'), + 'destination_name' => $this->string('destination_name'), 'foreign_currency_id' => null, 'foreign_currency_code' => null, 'foreign_amount' => null, @@ -161,11 +163,11 @@ class JournalFormRequest extends Request 'amount' => 'numeric|required|more:0', 'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id|nullable', 'category' => 'between:1,255|nullable', - 'source_account_id' => 'numeric|belongsToUser:accounts,id|nullable', - 'source_account_name' => 'between:1,255|nullable', - 'destination_account_id' => 'numeric|belongsToUser:accounts,id|nullable', - 'destination_account_name' => 'between:1,255|nullable', - 'piggy_bank_id' => 'between:1,255|nullable', + 'source_id' => 'numeric|belongsToUser:accounts,id|nullable', + 'source_name' => 'between:1,255|nullable', + 'destination_id' => 'numeric|belongsToUser:accounts,id|nullable', + 'destination_name' => 'between:1,255|nullable', + 'piggy_bank_id' => 'numeric|nullable', // foreign currency amounts 'native_amount' => 'numeric|more:0|nullable', @@ -179,6 +181,22 @@ class JournalFormRequest extends Request return $rules; } + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + $this->validNativeAmount($validator); + } + ); + } + /** * Inspired by https://www.youtube.com/watch?v=WwnI0RS6J5A. * @@ -193,17 +211,17 @@ class JournalFormRequest extends Request { switch ($what) { case strtolower(TransactionType::WITHDRAWAL): - $rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; - $rules['destination_account_name'] = 'between:1,255|nullable'; + $rules['source_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; + $rules['destination_name'] = 'between:1,255|nullable'; break; case strtolower(TransactionType::DEPOSIT): - $rules['source_account_name'] = 'between:1,255|nullable'; - $rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; + $rules['source_name'] = 'between:1,255|nullable'; + $rules['destination_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; break; case strtolower(TransactionType::TRANSFER): // this may not work: - $rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_account_id'; - $rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_account_id'; + $rules['source_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_id'; + $rules['destination_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_id'; break; default: @@ -212,4 +230,73 @@ class JournalFormRequest extends Request return $rules; } + + /** + * @param Validator $validator + */ + private function validNativeAmount(Validator $validator): void + { + $data = $validator->getData(); + $type = $data['what'] ?? 'invalid'; + Log::debug(sprintf('Type is %s', $type)); + if ($type === 'withdrawal') { + + $selectedCurrency = (int)($data['amount_currency_id_amount'] ?? 0); + $accountCurrency = (int)($data['source_account_currency'] ?? 0); + Log::debug(sprintf('Selected currency is %d, account currency is %d', $selectedCurrency, $accountCurrency)); + $nativeAmount = (string)($data['native_amount'] ?? ''); + if ($selectedCurrency !== $accountCurrency && '' === $nativeAmount + && $selectedCurrency !== 0 + && $accountCurrency !== 0 + ) { + Log::debug('ADD validation error on native_amount'); + $validator->errors()->add('native_amount', trans('validation.numeric_native')); + + return; + } + } + + // same thing for deposits: + if ($type === 'deposit') { + $selectedCurrency = (int)($data['amount_currency_id_amount'] ?? 0); + $accountCurrency = (int)($data['destination_account_currency'] ?? 0); + $nativeAmount = (string)($data['native_amount'] ?? ''); + if ($selectedCurrency !== $accountCurrency && '' === $nativeAmount + && $selectedCurrency !== 0 + && $accountCurrency !== 0 + ) { + $validator->errors()->add('native_amount', trans('validation.numeric_native')); + + return; + } + } + + // and for transfers + if ($type === 'transfer') { + + $sourceCurrency = (int)($data['source_account_currency'] ?? 0); + $destinationCurrency = (int)($data['destination_account_currency'] ?? 0); + $sourceAmount = (string)($data['source_amount'] ?? ''); + $destinationAmount = (string)($data['destination_amount'] ?? ''); + + Log::debug(sprintf('Source currency is %d, destination currency is %d', $sourceCurrency, $destinationCurrency)); + + if ($sourceCurrency !== $destinationCurrency && '' === $sourceAmount + && $sourceCurrency !== 0 + && $destinationCurrency !== 0 + ) { + $validator->errors()->add('source_amount', trans('validation.numeric_source')); + } + + if ($sourceCurrency !== $destinationCurrency && '' === $destinationAmount + && $sourceCurrency !== 0 + && $destinationCurrency !== 0 + ) { + $validator->errors()->add('destination_amount', trans('validation.numeric_destination')); + $validator->errors()->add('destination_amount', trans('validation.numeric', ['attribute' => 'destination_amount'])); + } + + return; + } + } } diff --git a/app/Http/Requests/MassEditJournalRequest.php b/app/Http/Requests/MassEditJournalRequest.php index e82e243c80..cd82765c6e 100644 --- a/app/Http/Requests/MassEditJournalRequest.php +++ b/app/Http/Requests/MassEditJournalRequest.php @@ -45,11 +45,11 @@ class MassEditJournalRequest extends Request // fixed return [ - 'description.*' => 'required|min:1,max:255', - 'source_account_id.*' => 'numeric|belongsToUser:accounts,id', - 'destination_account_id.*' => 'numeric|belongsToUser:accounts,id', - 'revenue_account' => 'max:255', - 'expense_account' => 'max:255', + 'description.*' => 'required|min:1,max:255', + 'source_id.*' => 'numeric|belongsToUser:accounts,id', + 'destination_id.*' => 'numeric|belongsToUser:accounts,id', + 'revenue_account' => 'max:255', + 'expense_account' => 'max:255', ]; } } diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php index fae23428f7..bd6488edd5 100644 --- a/app/Http/Requests/PiggyBankFormRequest.php +++ b/app/Http/Requests/PiggyBankFormRequest.php @@ -62,12 +62,12 @@ class PiggyBankFormRequest extends Request } $rules = [ - 'name' => $nameRule, - 'account_id' => 'required|belongsToUser:accounts', - 'targetamount' => 'required|numeric|more:0', - 'startdate' => 'date', - 'targetdate' => 'date|nullable', - 'order' => 'integer|min:1', + 'name' => $nameRule, + 'account_id' => 'required|belongsToUser:accounts', + 'targetamount' => 'required|numeric|more:0', + 'startdate' => 'date', + 'targetdate' => 'date|nullable', + 'order' => 'integer|min:1', ]; return $rules; diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php new file mode 100644 index 0000000000..7bebfacbce --- /dev/null +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -0,0 +1,256 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Requests; + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\TransactionType; +use FireflyIII\Rules\ValidRecurrenceRepetitionType; +use FireflyIII\Rules\ValidRecurrenceRepetitionValue; + +/** + * Class RecurrenceFormRequest + */ +class RecurrenceFormRequest extends Request +{ + + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow logged in users + return auth()->check(); + } + + /** + * @return array + * @throws FireflyException + */ + public function getAll(): array + { + $repetitionData = $this->parseRepetitionData(); + $return = [ + 'recurrence' => [ + 'type' => $this->string('transaction_type'), + 'title' => $this->string('title'), + 'description' => $this->string('recurring_description'), + 'first_date' => $this->date('first_date'), + 'repeat_until' => $this->date('repeat_until'), + 'repetitions' => $this->integer('repetitions'), + 'apply_rules' => $this->boolean('apply_rules'), + 'active' => $this->boolean('active'), + 'repetition_end' => $this->string('repetition_end'), + ], + 'transactions' => [ + [ + 'currency_id' => $this->integer('transaction_currency_id'), + 'currency_code' => null, + 'type' => $this->string('transaction_type'), + 'description' => $this->string('transaction_description'), + 'amount' => $this->string('amount'), + 'foreign_amount' => null, + 'foreign_currency_id' => null, + 'foreign_currency_code' => null, + 'budget_id' => $this->integer('budget_id'), + 'budget_name' => null, + 'category_id' => null, + 'category_name' => $this->string('category'), + + ], + ], + 'meta' => [ + // tags and piggy bank ID. + 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [], + 'piggy_bank_id' => $this->integer('piggy_bank_id'), + 'piggy_bank_name' => null, + ], + 'repetitions' => [ + [ + 'type' => $repetitionData['type'], + 'moment' => $repetitionData['moment'], + 'skip' => $this->integer('skip'), + 'weekend' => $this->integer('weekend'), + ], + ], + + ]; + + // fill in foreign currency data + if (null !== $this->float('foreign_amount')) { + $return['transactions'][0]['foreign_amount'] = $this->string('foreign_amount'); + $return['transactions'][0]['foreign_currency_id'] = $this->integer('foreign_currency_id'); + } + // default values: + $return['transactions'][0]['source_id'] = null; + $return['transactions'][0]['source_name'] = null; + $return['transactions'][0]['destination_id'] = null; + $return['transactions'][0]['destination_name'] = null; + // fill in source and destination account data + switch ($this->string('transaction_type')) { + default: + throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->string('transaction_type'))); + case 'withdrawal': + $return['transactions'][0]['source_id'] = $this->integer('source_id'); + $return['transactions'][0]['destination_name'] = $this->string('destination_name'); + break; + case 'deposit': + $return['transactions'][0]['source_name'] = $this->string('source_name'); + $return['transactions'][0]['destination_id'] = $this->integer('destination_id'); + break; + case 'transfer': + $return['transactions'][0]['source_id'] = $this->integer('source_id'); + $return['transactions'][0]['destination_id'] = $this->integer('destination_id'); + break; + } + + return $return; + } + + /** + * @return array + * @throws FireflyException + */ + public function rules(): array + { + $today = new Carbon; + $tomorrow = clone $today; + $tomorrow->addDay(); + $rules = [ + // mandatory info for recurrence. + 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', + 'first_date' => 'required|date|after:' . $today->format('Y-m-d'), + 'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'], + 'skip' => 'required|numeric|between:0,31', + + // optional for recurrence: + 'recurring_description' => 'between:0,65000', + 'active' => 'numeric|between:0,1', + 'apply_rules' => 'numeric|between:0,1', + + // mandatory for transaction: + 'transaction_description' => 'required|between:1,255', + 'transaction_type' => 'required|in:withdrawal,deposit,transfer', + 'transaction_currency_id' => 'required|exists:transaction_currencies,id', + 'amount' => 'numeric|required|more:0', + // mandatory account info: + 'source_id' => 'numeric|belongsToUser:accounts,id|nullable', + 'source_name' => 'between:1,255|nullable', + 'destination_id' => 'numeric|belongsToUser:accounts,id|nullable', + 'destination_name' => 'between:1,255|nullable', + + // foreign amount data: + 'foreign_amount' => 'nullable|more:0', + + // optional fields: + 'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id|nullable', + 'category' => 'between:1,255|nullable', + 'tags' => 'between:1,255|nullable', + ]; + if ($this->integer('foreign_currency_id') > 0) { + $rules['foreign_currency_id'] = 'exists:transaction_currencies,id'; + } + + // if ends after X repetitions, set another rule + if ($this->string('repetition_end') === 'times') { + $rules['repetitions'] = 'required|numeric|between:0,254'; + } + // if foreign amount, currency must be different. + if ($this->float('foreign_amount') !== 0.0) { + $rules['foreign_currency_id'] = 'exists:transaction_currencies,id|different:transaction_currency_id'; + } + + // if ends at date X, set another rule. + if ($this->string('repetition_end') === 'until_date') { + $rules['repeat_until'] = 'required|date|after:' . $tomorrow->format('Y-m-d'); + } + + // switchc on type to expand rules for source and destination accounts: + switch ($this->string('transaction_type')) { + case strtolower(TransactionType::WITHDRAWAL): + $rules['source_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; + $rules['destination_name'] = 'between:1,255|nullable'; + break; + case strtolower(TransactionType::DEPOSIT): + $rules['source_name'] = 'between:1,255|nullable'; + $rules['destination_id'] = 'required|exists:accounts,id|belongsToUser:accounts'; + break; + case strtolower(TransactionType::TRANSFER): + // this may not work: + $rules['source_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_id'; + $rules['destination_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_id'; + + break; + default: + throw new FireflyException(sprintf('Cannot handle transaction type of type "%s"', $this->string('transaction_type'))); // @codeCoverageIgnore + } + + // update some rules in case the user is editing a post: + /** @var Recurrence $recurrence */ + $recurrence = $this->route()->parameter('recurrence'); + if ($recurrence instanceof Recurrence) { + $rules['id'] = 'required|numeric|exists:recurrences,id'; + $rules['title'] = 'required|between:1,255|uniqueObjectForUser:recurrences,title,' . $recurrence->id; + $rules['first_date'] = 'required|date'; + } + + + return $rules; + } + + /** + * @return array + */ + private function parseRepetitionData(): array + { + $value = $this->string('repetition_type'); + $return = [ + 'type' => '', + 'moment' => '', + ]; + + if ($value === 'daily') { + $return['type'] = $value; + } + //monthly,17 + //ndom,3,7 + if (\in_array(substr($value, 0, 6), ['yearly', 'weekly'])) { + $return['type'] = substr($value, 0, 6); + $return['moment'] = substr($value, 7); + } + if (0 === strpos($value, 'monthly')) { + $return['type'] = substr($value, 0, 7); + $return['moment'] = substr($value, 8); + } + if (0 === strpos($value, 'ndom')) { + $return['type'] = substr($value, 0, 4); + $return['moment'] = substr($value, 5); + } + + return $return; + + + } +} \ No newline at end of file diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 1146b31017..c293b6237e 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -47,6 +47,16 @@ class Request extends FormRequest return 1 === (int)$this->input($field); } + /** + * @param string $field + * + * @return float + */ + public function float(string $field): float + { + return (float)$this->get($field); + } + /** * @param string $field * diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 148c24aa43..9ef8d49a60 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -32,7 +32,7 @@ class RuleFormRequest extends Request /** * @return bool */ - public function authorize() + public function authorize(): bool { // Only allow logged in users return auth()->check(); @@ -43,27 +43,52 @@ class RuleFormRequest extends Request */ public function getRuleData(): array { - return [ - 'title' => $this->string('title'), - 'rule_group_id' => $this->integer('rule_group_id'), - 'active' => $this->boolean('active'), - 'trigger' => $this->string('trigger'), - 'description' => $this->string('description'), - 'rule-triggers' => $this->get('rule-trigger'), - 'rule-trigger-values' => $this->get('rule-trigger-value'), - 'rule-trigger-stop' => $this->get('rule-trigger-stop'), - 'rule-actions' => $this->get('rule-action'), - 'rule-action-values' => $this->get('rule-action-value'), - 'rule-action-stop' => $this->get('rule-action-stop'), - 'stop_processing' => $this->boolean('stop_processing'), - 'strict' => $this->boolean('strict'), + $data = [ + 'title' => $this->string('title'), + 'rule_group_id' => $this->integer('rule_group_id'), + 'active' => $this->boolean('active'), + 'trigger' => $this->string('trigger'), + 'description' => $this->string('description'), + 'stop-processing' => $this->boolean('stop_processing'), + 'strict' => $this->boolean('strict'), + 'rule-triggers' => [], + 'rule-actions' => [], ]; + $triggers = $this->get('rule-trigger'); + $triggerValues = $this->get('rule-trigger-value'); + $triggerStop = $this->get('rule-trigger-stop'); + + $actions = $this->get('rule-action'); + $actionValues = $this->get('rule-action-value'); + $actionStop = $this->get('rule-action-stop'); + + if (\is_array($triggers)) { + foreach ($triggers as $index => $value) { + $data['rule-triggers'][] = [ + 'name' => $value, + 'value' => $triggerValues[$index] ?? '', + 'stop-processing' => (int)($triggerStop[$index] ?? 0) === 1, + ]; + } + } + + if (\is_array($actions)) { + foreach ($actions as $index => $value) { + $data['rule-actions'][] = [ + 'name' => $value, + 'value' => $actionValues[$index] ?? '', + 'stop-processing' => (int)($actionStop[$index] ?? 0) === 1, + ]; + } + } + + return $data; } /** * @return array */ - public function rules() + public function rules(): array { /** @var RuleRepositoryInterface $repository */ $repository = app(RuleRepositoryInterface::class); diff --git a/app/Http/Requests/SplitJournalFormRequest.php b/app/Http/Requests/SplitJournalFormRequest.php index ce99023a1f..5e1a4d062f 100644 --- a/app/Http/Requests/SplitJournalFormRequest.php +++ b/app/Http/Requests/SplitJournalFormRequest.php @@ -64,16 +64,16 @@ class SplitJournalFormRequest extends Request foreach ($this->get('transactions') as $index => $transaction) { switch ($data['type']) { case 'withdrawal': - $sourceId = $this->integer('journal_source_account_id'); + $sourceId = $this->integer('journal_source_id'); $destinationName = $transaction['destination_name'] ?? ''; break; case 'deposit': $sourceName = $transaction['source_name'] ?? ''; - $destinationId = $this->integer('journal_destination_account_id'); + $destinationId = $this->integer('journal_destination_id'); break; case 'transfer': - $sourceId = $this->integer('journal_source_account_id'); - $destinationId = $this->integer('journal_destination_account_id'); + $sourceId = $this->integer('journal_source_id'); + $destinationId = $this->integer('journal_destination_id'); break; } $foreignAmount = $transaction['foreign_amount'] ?? null; @@ -91,7 +91,7 @@ class SplitJournalFormRequest extends Request 'currency_id' => $this->integer('journal_currency_id'), 'currency_code' => null, 'description' => $transaction['transaction_description'] ?? '', - 'amount' => $transaction['amount'], + 'amount' => $transaction['amount'] ?? '', 'budget_id' => (int)($transaction['budget_id'] ?? 0.0), 'budget_name' => null, 'category_id' => null, @@ -109,23 +109,23 @@ class SplitJournalFormRequest extends Request public function rules(): array { return [ - 'what' => 'required|in:withdrawal,deposit,transfer', - 'journal_description' => 'required|between:1,255', - 'id' => 'numeric|belongsToUser:transaction_journals,id', - 'journal_source_account_id' => 'numeric|belongsToUser:accounts,id', - 'journal_source_account_name.*' => 'between:1,255', - 'journal_currency_id' => 'required|exists:transaction_currencies,id', - 'date' => 'required|date', - 'interest_date' => 'date|nullable', - 'book_date' => 'date|nullable', - 'process_date' => 'date|nullable', - 'transactions.*.transaction_description' => 'required|between:1,255', - 'transactions.*.destination_account_id' => 'numeric|belongsToUser:accounts,id', - 'transactions.*.destination_name' => 'between:1,255|nullable', - 'transactions.*.amount' => 'required|numeric', - 'transactions.*.budget_id' => 'belongsToUser:budgets,id', - 'transactions.*.category_name' => 'between:1,255|nullable', - 'transactions.*.piggy_bank_id' => 'between:1,255|nullable', + 'what' => 'required|in:withdrawal,deposit,transfer', + 'journal_description' => 'required|between:1,255', + 'id' => 'numeric|belongsToUser:transaction_journals,id', + 'journal_source_id' => 'numeric|belongsToUser:accounts,id', + 'journal_source_name.*' => 'between:1,255', + 'journal_currency_id' => 'required|exists:transaction_currencies,id', + 'date' => 'required|date', + 'interest_date' => 'date|nullable', + 'book_date' => 'date|nullable', + 'process_date' => 'date|nullable', + 'transactions.*.transaction_description' => 'required|between:1,255', + 'transactions.*.destination_id' => 'numeric|belongsToUser:accounts,id', + 'transactions.*.destination_name' => 'between:1,255|nullable', + 'transactions.*.amount' => 'required|numeric', + 'transactions.*.budget_id' => 'belongsToUser:budgets,id', + 'transactions.*.category_name' => 'between:1,255|nullable', + 'transactions.*.piggy_bank_id' => 'numeric|nullable', ]; } @@ -155,8 +155,8 @@ class SplitJournalFormRequest extends Request /** @var array $array */ foreach ($transactions as $array) { if ($array['destination_id'] !== null && $array['source_id'] !== null && $array['destination_id'] === $array['source_id']) { - $validator->errors()->add('journal_source_account_id', trans('validation.source_equals_destination')); - $validator->errors()->add('journal_destination_account_id', trans('validation.source_equals_destination')); + $validator->errors()->add('journal_source_id', trans('validation.source_equals_destination')); + $validator->errors()->add('journal_destination_id', trans('validation.source_equals_destination')); } } diff --git a/app/Import/Configuration/BunqConfigurator.php b/app/Import/Configuration/BunqConfigurator.php deleted file mode 100644 index a115f8e933..0000000000 --- a/app/Import/Configuration/BunqConfigurator.php +++ /dev/null @@ -1,214 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Import\Configuration; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\ImportJob; -use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Support\Import\Configuration\Bunq\HaveAccounts; -use Log; - -/** - * @deprecated - * @codeCoverageIgnore - * Class BunqConfigurator. - */ -class BunqConfigurator implements ConfiguratorInterface -{ - /** @var ImportJob */ - private $job; - - /** @var ImportJobRepositoryInterface */ - private $repository; - - /** @var string */ - private $warning = ''; - - /** - * ConfiguratorInterface constructor. - */ - public function __construct() - { - } - - /** - * Store any data from the $data array into the job. - * - * @param array $data - * - * @return bool - * - * @throws FireflyException - */ - public function configureJob(array $data): bool - { - if (null === $this->job) { - throw new FireflyException('Cannot call configureJob() without a job.'); - } - $stage = $this->getConfig()['stage'] ?? 'initial'; - Log::debug(sprintf('in getNextData(), for stage "%s".', $stage)); - - switch ($stage) { - case 'have-accounts': - /** @var HaveAccounts $class */ - $class = app(HaveAccounts::class); - $class->setJob($this->job); - $class->storeConfiguration($data); - - // update job for next step and set to "configured". - $config = $this->getConfig(); - $config['stage'] = 'have-account-mapping'; - $this->repository->setConfiguration($this->job, $config); - - return true; - default: - throw new FireflyException(sprintf('Cannot store configuration when job is in state "%s"', $stage)); - break; - } - } - - /** - * Return the data required for the next step in the job configuration. - * - * @return array - * - * @throws FireflyException - */ - public function getNextData(): array - { - if (null === $this->job) { - throw new FireflyException('Cannot call configureJob() without a job.'); - } - $config = $this->getConfig(); - $stage = $config['stage'] ?? 'initial'; - - Log::debug(sprintf('in getNextData(), for stage "%s".', $stage)); - - switch ($stage) { - case 'have-accounts': - /** @var HaveAccounts $class */ - $class = app(HaveAccounts::class); - $class->setJob($this->job); - - return $class->getData(); - default: - return []; - } - } - - /** - * @return string - * - * @throws FireflyException - */ - public function getNextView(): string - { - if (null === $this->job) { - throw new FireflyException('Cannot call configureJob() without a job.'); - } - $stage = $this->getConfig()['stage'] ?? 'initial'; - - Log::debug(sprintf('getNextView: in getNextView(), for stage "%s".', $stage)); - switch ($stage) { - case 'have-accounts': - return 'import.bunq.accounts'; - default: - return ''; - } - } - - /** - * Return possible warning to user. - * - * @return string - */ - public function getWarningMessage(): string - { - return $this->warning; - } - - /** - * @return bool - * - * @throws FireflyException - */ - public function isJobConfigured(): bool - { - if (null === $this->job) { - throw new FireflyException('Cannot call configureJob() without a job.'); - } - $stage = $this->getConfig()['stage'] ?? 'initial'; - - Log::debug(sprintf('in isJobConfigured(), for stage "%s".', $stage)); - switch ($stage) { - case 'have-accounts': - Log::debug('isJobConfigured returns false'); - - return false; - default: - Log::debug('isJobConfigured returns true'); - - return true; - } - } - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job): void - { - // make repository - $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); - - // set default config: - $defaultConfig = [ - 'is-redirected' => false, - 'stage' => 'initial', - 'auto-start' => true, - 'apply-rules' => true, - ]; - $currentConfig = $this->repository->getConfiguration($job); - $finalConfig = array_merge($defaultConfig, $currentConfig); - - // set default extended status: - $extendedStatus = $this->repository->getExtendedStatus($job); - $extendedStatus['steps'] = 8; - - // save to job: - $job = $this->repository->setConfiguration($job, $finalConfig); - $job = $this->repository->setExtendedStatus($job, $extendedStatus); - $this->job = $job; - - } - - /** - * Shorthand method. - * - * @return array - */ - private function getConfig(): array - { - return $this->repository->getConfiguration($this->job); - } -} diff --git a/app/Import/Configuration/ConfiguratorInterface.php b/app/Import/Configuration/ConfiguratorInterface.php deleted file mode 100644 index a1e961b0ec..0000000000 --- a/app/Import/Configuration/ConfiguratorInterface.php +++ /dev/null @@ -1,80 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Import\Configuration; - -use FireflyIII\Models\ImportJob; - -/** - * @deprecated - * @codeCoverageIgnore - * Interface ConfiguratorInterface. - */ -interface ConfiguratorInterface -{ - /** - * ConfiguratorInterface constructor. - */ - public function __construct(); - - /** - * Store any data from the $data array into the job. - * - * @param array $data - * - * @return bool - */ - public function configureJob(array $data): bool; - - /** - * Return the data required for the next step in the job configuration. - * - * @return array - */ - public function getNextData(): array; - - /** - * Returns the view of the next step in the job configuration. - * - * @return string - */ - public function getNextView(): string; - - /** - * Return possible warning to user. - * - * @return string - */ - public function getWarningMessage(): string; - - /** - * Returns true when the initial configuration for this job is complete. - * - * @return bool - */ - public function isJobConfigured(): bool; - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job); -} diff --git a/app/Import/Converter/Amount.php b/app/Import/Converter/Amount.php index 21b3769c0c..070842a050 100644 --- a/app/Import/Converter/Amount.php +++ b/app/Import/Converter/Amount.php @@ -98,8 +98,31 @@ class Amount implements ConverterInterface $value = str_replace($search, '', $value); Log::debug(sprintf('No decimal character found. Converted amount from "%s" to "%s".', $original, $value)); } + if ($value{0} === '.') { + $value = '0' . $value; + } - return (string)number_format(round(floatval($value), 12), 12, '.', ''); + if (is_numeric($value)) { + Log::debug(sprintf('Final NUMERIC value is: "%s"', $value)); + + return $value; + } + Log::debug(sprintf('Final value is: "%s"', $value)); + $formatted = sprintf('%01.12f', $value); + Log::debug(sprintf('Is formatted to : "%s"', $formatted)); + + return $formatted; + } + + private function bcround($number, $scale = 0) + { + $fix = "5"; + for ($i = 0; $i < $scale; $i++) { + $fix = "0$fix"; + } + $number = bcadd($number, "0.$fix", $scale + 1); + + return bcdiv($number, "1.0", $scale); } /** @@ -109,6 +132,11 @@ class Amount implements ConverterInterface */ private function stripAmount(string $value): string { + if (0 === strpos($value, '--')) { + $value = substr($value, 2); + } + + $str = preg_replace('/[^\-\(\)\.\,0-9 ]/', '', $value); $len = \strlen($str); if ('(' === $str[0] && ')' === $str[$len - 1]) { diff --git a/app/Import/Converter/RabobankDebitCredit.php b/app/Import/Converter/RabobankDebitCredit.php index d426e52b5b..764a7f907e 100644 --- a/app/Import/Converter/RabobankDebitCredit.php +++ b/app/Import/Converter/RabobankDebitCredit.php @@ -44,7 +44,7 @@ class RabobankDebitCredit implements ConverterInterface return -1; } // old format: - if('A' === $value) { + if ('A' === $value) { Log::debug('Return -1'); return -1; diff --git a/app/Import/JobConfiguration/FakeJobConfiguration.php b/app/Import/JobConfiguration/FakeJobConfiguration.php index 81a96adfd7..3812a78d59 100644 --- a/app/Import/JobConfiguration/FakeJobConfiguration.php +++ b/app/Import/JobConfiguration/FakeJobConfiguration.php @@ -106,6 +106,7 @@ class FakeJobConfiguration implements JobConfigurationInterface /** * Return the data required for the next step in the job configuration. + * * @codeCoverageIgnore * @return array */ @@ -144,6 +145,7 @@ class FakeJobConfiguration implements JobConfigurationInterface if (strtolower($album) !== 'station to station' && $this->importJob->stage !== 'new') { return 'import.fake.enter-album'; } + return 'impossible-view'; // @codeCoverageIgnore } @@ -152,7 +154,7 @@ class FakeJobConfiguration implements JobConfigurationInterface */ public function setImportJob(ImportJob $importJob): void { - $this->importJob = $importJob; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($importJob->user); } diff --git a/app/Import/JobConfiguration/FileJobConfiguration.php b/app/Import/JobConfiguration/FileJobConfiguration.php index cfc1d18966..5820b38951 100644 --- a/app/Import/JobConfiguration/FileJobConfiguration.php +++ b/app/Import/JobConfiguration/FileJobConfiguration.php @@ -28,10 +28,10 @@ namespace FireflyIII\Import\JobConfiguration; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Support\Import\JobConfiguration\File\FileConfigurationInterface; use FireflyIII\Support\Import\JobConfiguration\File\ConfigureMappingHandler; use FireflyIII\Support\Import\JobConfiguration\File\ConfigureRolesHandler; use FireflyIII\Support\Import\JobConfiguration\File\ConfigureUploadHandler; +use FireflyIII\Support\Import\JobConfiguration\File\FileConfigurationInterface; use FireflyIII\Support\Import\JobConfiguration\File\NewFileJobHandler; use Illuminate\Support\MessageBag; @@ -120,7 +120,7 @@ class FileJobConfiguration implements JobConfigurationInterface */ public function setImportJob(ImportJob $importJob): void { - $this->importJob = $importJob; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($importJob->user); } diff --git a/app/Import/Prerequisites/BunqPrerequisites.php b/app/Import/Prerequisites/BunqPrerequisites.php index ea39453f3a..21334ced35 100644 --- a/app/Import/Prerequisites/BunqPrerequisites.php +++ b/app/Import/Prerequisites/BunqPrerequisites.php @@ -116,10 +116,11 @@ class BunqPrerequisites implements PrerequisitesInterface $environment = $this->getBunqEnvironment(); $deviceDescription = 'Firefly III v' . config('firefly.version'); $permittedIps = [$externalIP]; + Log::debug(sprintf('Environment for bunq is %s', $environment->getChoiceString())); try { /** @var ApiContext $object */ - $object = app(ApiContext::class); + $object = app(ApiContext::class); $apiContext = $object->create($environment, $apiKey, $deviceDescription, $permittedIps); } catch (FireflyException $e) { $messages = new MessageBag(); diff --git a/app/Import/Prerequisites/FakePrerequisites.php b/app/Import/Prerequisites/FakePrerequisites.php index ac8c07affb..86642e8066 100644 --- a/app/Import/Prerequisites/FakePrerequisites.php +++ b/app/Import/Prerequisites/FakePrerequisites.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Import\Prerequisites; use FireflyIII\User; -use Illuminate\Http\Request; use Illuminate\Support\MessageBag; /** diff --git a/app/Import/Prerequisites/PrerequisitesInterface.php b/app/Import/Prerequisites/PrerequisitesInterface.php index 588821eeca..f3f82ae5be 100644 --- a/app/Import/Prerequisites/PrerequisitesInterface.php +++ b/app/Import/Prerequisites/PrerequisitesInterface.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Import\Prerequisites; use FireflyIII\User; -use Illuminate\Http\Request; use Illuminate\Support\MessageBag; /** diff --git a/app/Import/Routine/SpectreRoutine.php b/app/Import/Routine/SpectreRoutine.php index 1c51d8d697..83aea2664a 100644 --- a/app/Import/Routine/SpectreRoutine.php +++ b/app/Import/Routine/SpectreRoutine.php @@ -25,8 +25,8 @@ namespace FireflyIII\Import\Routine; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler; use FireflyIII\Support\Import\Routine\Spectre\StageAuthenticatedHandler; +use FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler; use FireflyIII\Support\Import\Routine\Spectre\StageNewHandler; use Log; diff --git a/app/Import/Storage/ImportArrayStorage.php b/app/Import/Storage/ImportArrayStorage.php index 01315fbe9f..15d5ae58fa 100644 --- a/app/Import/Storage/ImportArrayStorage.php +++ b/app/Import/Storage/ImportArrayStorage.php @@ -108,6 +108,8 @@ class ImportArrayStorage $this->setStatus('rules_applied'); } + app('preferences')->mark(); + return $collection; } @@ -215,7 +217,7 @@ class ImportArrayStorage $collector = app(JournalCollectorInterface::class); $collector->setUser($this->importJob->user); $collector->setAllAssetAccounts() - ->ignoreCache() + ->ignoreCache() ->setTypes([TransactionType::TRANSFER]) ->withOpposingAccount(); $collector->removeFilter(InternalTransferFilter::class); @@ -301,7 +303,7 @@ class ImportArrayStorage 'existing' => $existingId, 'description' => $transaction['description'] ?? '', 'amount' => $transaction['transactions'][0]['amount'] ?? 0, - 'date' => isset($transaction['date']) ? $transaction['date'] : '', + 'date' => $transaction['date'] ?? '', ] ); @@ -411,7 +413,14 @@ class ImportArrayStorage $store['date'] = Carbon::createFromFormat('Y-m-d', $store['date']); $store['description'] = $store['description'] === '' ? '(empty description)' : $store['description']; // store the journal. - $journal = $this->journalRepos->store($store); + try { + $journal = $this->journalRepos->store($store); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + $this->repository->addErrorMessage($this->importJob, sprintf('Row #%d could not be imported. %s', $index, $e->getMessage())); + continue; + } Log::debug(sprintf('Stored as journal #%d', $journal->id)); $collection->push($journal); } diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php new file mode 100644 index 0000000000..d4a09795af --- /dev/null +++ b/app/Jobs/CreateRecurringTransactions.php @@ -0,0 +1,422 @@ +startOfDay(); + $this->date = $date; + $this->repository = app(RecurringRepositoryInterface::class); + $this->journalRepository = app(JournalRepositoryInterface::class); + + } + + /** + * Execute the job. + * + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function handle(): void + { + Log::debug(sprintf('Now at start of CreateRecurringTransactions() job for %s.', $this->date->format('D d M Y'))); + $recurrences = $this->repository->getAll(); + Log::debug(sprintf('Count of collection is %d', $recurrences->count())); + + $result = []; + + /** @var Collection $filtered */ + $filtered = $recurrences->filter( + function (Recurrence $recurrence) { + return $this->validRecurrence($recurrence); + + } + ); + Log::debug(sprintf('Left after filtering is %d', $filtered->count())); + /** @var Recurrence $recurrence */ + foreach ($filtered as $recurrence) { + if (!isset($result[$recurrence->user_id])) { + $result[$recurrence->user_id] = new Collection; + } + + $this->repository->setUser($recurrence->user); + $this->journalRepository->setUser($recurrence->user); + Log::debug(sprintf('Now at recurrence #%d', $recurrence->id)); + $created = $this->handleRepetitions($recurrence); + Log::debug(sprintf('Done with recurrence #%d', $recurrence->id)); + $result[$recurrence->user_id] = $result[$recurrence->user_id]->merge($created); + + // apply rules: + $this->applyRules($recurrence->user, $created); + } + + Log::debug('Now running report thing.'); + // will now send email to users. + foreach ($result as $userId => $journals) { + event(new RequestedReportOnJournals($userId, $journals)); + } + + Log::debug('Done with handle()'); + } + + /** + * Return recurring transaction is active. + * + * @param Recurrence $recurrence + * + * @return bool + */ + private function active(Recurrence $recurrence): bool + { + return $recurrence->active; + } + + /** + * @param User $user + * @param Collection $journals + */ + private function applyRules(User $user, Collection $journals): void + { + $userId = $user->id; + if (!isset($this->rules[$userId])) { + $this->rules[$userId] = $this->getRules($user); + } + // run the rules: + if ($this->rules[$userId]->count() > 0) { + /** @var TransactionJournal $journal */ + foreach ($journals as $journal) { + $this->rules[$userId]->each( + function (Rule $rule) use ($journal) { + Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id)); + $processor = Processor::make($rule); + $processor->handleTransactionJournal($journal); + if ($rule->stop_processing) { + return; + } + } + ); + } + } + } + + /** + * @param array $occurrences + * + * @return array + */ + private function debugArray(array $occurrences): array + { + $return = []; + foreach ($occurrences as $entry) { + $return[] = $entry->format('Y-m-d'); + } + + return $return; + } + + /** + * @param User $user + * + * @return Collection + */ + private function getRules(User $user): Collection + { + /** @var RuleRepositoryInterface $repository */ + $repository = app(RuleRepositoryInterface::class); + $repository->setUser($user); + $set = $repository->getForImport(); + + Log::debug(sprintf('Found %d user rules.', $set->count())); + + return $set; + } + + /** + * @param Recurrence $recurrence + * + * @return Carbon + */ + private function getStartDate(Recurrence $recurrence): Carbon + { + $startDate = clone $recurrence->first_date; + if (null !== $recurrence->latest_date && $recurrence->latest_date->gte($startDate)) { + $startDate = clone $recurrence->latest_date; + } + + return $startDate; + } + + /** + * @param Recurrence $recurrence + * + * @return array + */ + private function getTransactionData(Recurrence $recurrence): array + { + $transactions = $recurrence->recurrenceTransactions()->get(); + $return = []; + /** @var RecurrenceTransaction $transaction */ + foreach ($transactions as $index => $transaction) { + $single = [ + 'currency_id' => $transaction->transaction_currency_id, + 'currency_code' => null, + 'description' => null, + 'amount' => $transaction->amount, + 'budget_id' => $this->repository->getBudget($transaction), + 'budget_name' => null, + 'category_id' => null, + 'category_name' => $this->repository->getCategory($transaction), + 'source_id' => $transaction->source_id, + 'source_name' => null, + 'destination_id' => $transaction->destination_id, + 'destination_name' => null, + 'foreign_currency_id' => $transaction->foreign_currency_id, + 'foreign_currency_code' => null, + 'foreign_amount' => $transaction->foreign_amount, + 'reconciled' => false, + 'identifier' => $index, + ]; + $return[] = $single; + } + + return $return; + } + + /** + * @param Recurrence $recurrence + * @param array $occurrences + * + * @return Collection + * @throws \FireflyIII\Exceptions\FireflyException + */ + private function handleOccurrences(Recurrence $recurrence, array $occurrences): Collection + { + $collection = new Collection; + /** @var Carbon $date */ + foreach ($occurrences as $date) { + Log::debug(sprintf('Now at date %s.', $date->format('Y-m-d'))); + if ($date->ne($this->date)) { + Log::debug(sprintf('%s is not not today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d'))); + + continue; + } + Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d'))); + + // count created journals on THIS day. + $created = $this->repository->getJournals($recurrence, $date, $date); + if ($created->count() > 0) { + Log::info(sprintf('Already created %d journal(s) for date %s', $created->count(), $date->format('Y-m-d'))); + continue; + } + + // create transaction array and send to factory. + $array = [ + 'type' => $recurrence->transactionType->type, + 'date' => $date, + 'tags' => $this->repository->getTags($recurrence), + 'user' => $recurrence->user_id, + 'notes' => trans('firefly.created_from_recurrence', ['id' => $recurrence->id, 'title' => $recurrence->title]), + + // journal data: + 'description' => $recurrence->recurrenceTransactions()->first()->description, + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'bill_id' => null, + 'bill_name' => null, + 'recurrence_id' => (int)$recurrence->id, + + // transaction data: + 'transactions' => $this->getTransactionData($recurrence), + ]; + $journal = $this->journalRepository->store($array); + Log::info(sprintf('Created new journal #%d', $journal->id)); + + $collection->push($journal); + // update recurring thing: + $recurrence->latest_date = $date; + $recurrence->save(); + } + + return $collection; + } + + /** + * Separate method that will loop all repetitions and do something with it. Will return + * all created transaction journals. + * + * @param Recurrence $recurrence + * + * @return Collection + * + * @throws \FireflyIII\Exceptions\FireflyException + */ + private function handleRepetitions(Recurrence $recurrence): Collection + { + $collection = new Collection; + /** @var RecurrenceRepetition $repetition */ + foreach ($recurrence->recurrenceRepetitions as $repetition) { + Log::debug( + sprintf( + 'Now repeating %s with value "%s", skips every %d time(s)', $repetition->repetition_type, $repetition->repetition_moment, + $repetition->repetition_skip + ) + ); + + // start looping from $startDate to today perhaps we have a hit? + // add two days to $this->date so we always include the weekend. + $includeWeekend = clone $this->date; + $includeWeekend->addDays(2); + $occurrences = $this->repository->getOccurrencesInRange($repetition, $recurrence->first_date, $includeWeekend); + Log::debug( + sprintf( + 'Calculated %d occurrences between %s and %s', + \count($occurrences), + $recurrence->first_date->format('Y-m-d'), + $includeWeekend->format('Y-m-d') + ), $this->debugArray($occurrences) + ); + unset($includeWeekend); + + $result = $this->handleOccurrences($recurrence, $occurrences); + $collection = $collection->merge($result); + } + + return $collection; + } + + /** + * @param Recurrence $recurrence + * + * @return bool + */ + private function hasFiredToday(Recurrence $recurrence): bool + { + return null !== $recurrence->latest_date && $recurrence->latest_date->eq($this->date); + } + + /** + * @param $recurrence + * + * @return bool + */ + private function hasNotStartedYet(Recurrence $recurrence): bool + { + $startDate = $this->getStartDate($recurrence); + + return $startDate->gt($this->date); + } + + /** + * Return true if the $repeat_until date is in the past. + * + * @param Recurrence $recurrence + * + * @return bool + */ + private function repeatUntilHasPassed(Recurrence $recurrence): bool + { + // date has passed + return null !== $recurrence->repeat_until && $recurrence->repeat_until->lt($this->date); + } + + /** + * @param Recurrence $recurrence + * + * @return bool + */ + private function validRecurrence(Recurrence $recurrence): bool + { + // is not active. + if (!$this->active($recurrence)) { + Log::info(sprintf('Recurrence #%d is not active. Skipped.', $recurrence->id)); + + return false; + } + + // has repeated X times. + $journals = $this->repository->getJournals($recurrence, null, null); + if ($recurrence->repetitions !== 0 && $journals->count() >= $recurrence->repetitions) { + Log::info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $recurrence->repetitions)); + + return false; + } + + // is no longer running + if ($this->repeatUntilHasPassed($recurrence)) { + Log::info( + sprintf( + 'Recurrence #%d was set to run until %s, and today\'s date is %s. Skipped.', + $recurrence->id, + $recurrence->repeat_until->format('Y-m-d'), + $this->date->format('Y-m-d') + ) + ); + + return false; + } + + // first_date is in the future + if ($this->hasNotStartedYet($recurrence)) { + Log::info( + sprintf( + 'Recurrence #%d is set to run on %s, and today\'s date is %s. Skipped.', + $recurrence->id, + $recurrence->first_date->format('Y-m-d'), + $this->date->format('Y-m-d') + ) + ); + + return false; + } + + // already fired today (with success): + if ($this->hasFiredToday($recurrence)) { + Log::info(sprintf('Recurrence #%d has already fired today. Skipped.', $recurrence->id)); + + return false; + } + + return true; + + } +} diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php new file mode 100644 index 0000000000..a483625c1a --- /dev/null +++ b/app/Mail/ReportNewJournalsMail.php @@ -0,0 +1,77 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Mail; + +use Illuminate\Bus\Queueable; +use Illuminate\Mail\Mailable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Collection; + +/** + * Class ReportNewJournalsMail. + * + * Sends a list of newly created journals to the user. + */ +class ReportNewJournalsMail extends Mailable +{ + use Queueable, SerializesModels; + + /** @var string Email address of the user */ + public $email; + /** @var string IP address of user (if known) */ + public $ipAddress; + + /** @var Collection A collection of journals */ + public $journals; + + /** + * ConfirmEmailChangeMail constructor. + * + * @param string $email + * @param string $ipAddress + * @param Collection $journals + */ + public function __construct(string $email, string $ipAddress, Collection $journals) + { + $this->email = $email; + $this->ipAddress = $ipAddress; + $this->journals = $journals; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): self + { + $subject = $this->journals->count() === 1 + ? 'Firefly III has created a new transaction' + : sprintf( + 'Firefly III has created new %d transactions', $this->journals->count() + ); + + return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text') + ->subject($subject); + } +} diff --git a/app/Models/Account.php b/app/Models/Account.php index 1e79b0aa2c..adcfb61dc7 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -38,10 +38,13 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Account. * - * @property int $id - * @property string $name - * @property string $iban + * @property int $id + * @property string $name + * @property string $iban * @property AccountType $accountType + * @property bool $active + * @property string $virtual_balance + * @property User $user */ class Account extends Model { diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index aa5bb522fd..7635b179dc 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -24,10 +24,10 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use FireflyIII\Models\Account; /** * Class AccountMeta. + * @property string $data */ class AccountMeta extends Model { diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 9729d5a34f..de1d5794cd 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -24,10 +24,10 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; -use FireflyIII\Models\Account; /** * Class AccountType. + * * @property string $type * */ diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 2c1a1e61b6..c0d71a8fa7 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -22,16 +22,31 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Crypt; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\User; /** * Class Attachment. + * + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $attachable_type + * @property string $md5 + * @property string $filename + * @property string $title + * @property string $description + * @property string $notes + * @property string $mime + * @property int $size + * @property User $user + * @property bool $uploaded */ class Attachment extends Model { @@ -100,9 +115,9 @@ class Attachment extends Model * @return null|string * @throws \Illuminate\Contracts\Encryption\DecryptException */ - public function getDescriptionAttribute($value) + public function getDescriptionAttribute($value): ?string { - if (null === $value || 0 === \strlen($value)) { + if (null === $value || '' === $value) { return null; } @@ -116,9 +131,9 @@ class Attachment extends Model * @return null|string * @throws \Illuminate\Contracts\Encryption\DecryptException */ - public function getFilenameAttribute($value) + public function getFilenameAttribute($value): ?string { - if (null === $value || 0 === \strlen($value)) { + if (null === $value || '' === $value) { return null; } @@ -132,9 +147,9 @@ class Attachment extends Model * @return null|string * @throws \Illuminate\Contracts\Encryption\DecryptException */ - public function getMimeAttribute($value) + public function getMimeAttribute($value): ?string { - if (null === $value || 0 === \strlen($value)) { + if (null === $value || '' === $value) { return null; } @@ -148,9 +163,9 @@ class Attachment extends Model * @return null|string * @throws \Illuminate\Contracts\Encryption\DecryptException */ - public function getTitleAttribute($value) + public function getTitleAttribute($value): ?string { - if (null === $value || 0 === \strlen($value)) { + if (null === $value || '' === $value) { return null; } @@ -169,13 +184,14 @@ class Attachment extends Model /** * @codeCoverageIgnore * - * @param string $value - * - * @throws \Illuminate\Contracts\Encryption\EncryptException + * @param string|null $value */ - public function setDescriptionAttribute(string $value) + public function setDescriptionAttribute(string $value = null): void { - $this->attributes['description'] = Crypt::encrypt($value); + if (null !== $value) { + $this->attributes['description'] = Crypt::encrypt($value); + } + } /** @@ -185,7 +201,7 @@ class Attachment extends Model * * @throws \Illuminate\Contracts\Encryption\EncryptException */ - public function setFilenameAttribute(string $value) + public function setFilenameAttribute(string $value): void { $this->attributes['filename'] = Crypt::encrypt($value); } @@ -197,7 +213,7 @@ class Attachment extends Model * * @throws \Illuminate\Contracts\Encryption\EncryptException */ - public function setMimeAttribute(string $value) + public function setMimeAttribute(string $value): void { $this->attributes['mime'] = Crypt::encrypt($value); } @@ -209,9 +225,11 @@ class Attachment extends Model * * @throws \Illuminate\Contracts\Encryption\EncryptException */ - public function setTitleAttribute(string $value) + public function setTitleAttribute(string $value = null): void { - $this->attributes['title'] = Crypt::encrypt($value); + if (null !== $value) { + $this->attributes['title'] = Crypt::encrypt($value); + } } /** diff --git a/app/Models/AvailableBudget.php b/app/Models/AvailableBudget.php index 1e74d00af1..6cfbc53862 100644 --- a/app/Models/AvailableBudget.php +++ b/app/Models/AvailableBudget.php @@ -22,14 +22,25 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; -use FireflyIII\User; -use FireflyIII\Models\TransactionCurrency; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class AvailableBudget. + * + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property User $user + * @property TransactionCurrency $transactionCurrency + * @property int $transaction_currency_id + * @property Carbon $start_date + * @property Carbon $end_date + * @property string $amount */ class AvailableBudget extends Model { @@ -50,11 +61,29 @@ class AvailableBudget extends Model /** @var array */ protected $fillable = ['user_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date']; + /** + * @param string $value + * + * @return AvailableBudget + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public static function routeBinder(string $value): AvailableBudget + { + if (auth()->check()) { + $availableBudgetId = (int)$value; + $availableBudget = auth()->user()->availableBudgets()->find($availableBudgetId); + if (null !== $availableBudget) { + return $availableBudget; + } + } + throw new NotFoundHttpException; + } + /** * @codeCoverageIgnore * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function transactionCurrency() + public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } diff --git a/app/Models/Bill.php b/app/Models/Bill.php index bf4c01ff12..6693972b20 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -22,21 +22,34 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Crypt; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Bill. * - * @property bool $active - * @property int $transaction_currency_id - * @property string $amount_min - * @property string $amount_max + * @property bool $active + * @property int $transaction_currency_id + * @property string $amount_min + * @property string $amount_max + * @property int $id + * @property string $name + * @property Collection $notes + * @property TransactionCurrency $transactionCurrency + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Carbon $date + * @property string $repeat_freq + * @property int $skip + * @property bool $automatch + * @property User $user */ class Bill extends Model { diff --git a/app/Models/Budget.php b/app/Models/Budget.php index 6fc3e3d6f4..d3c0ff820d 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -23,17 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Models; use Crypt; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\User; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\BudgetLimit; /** * Class Budget. + * + * @property int $id + * @property string $name + * @property bool $active */ class Budget extends Model { diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 615c2d90ee..7559c258b2 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -22,12 +22,21 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\Budget; /** * Class BudgetLimit. + * + * @property Budget $budget + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property Carbon $start_date + * @property Carbon $end_date + * @property string $amount */ class BudgetLimit extends Model { @@ -42,8 +51,8 @@ class BudgetLimit extends Model 'updated_at' => 'datetime', 'start_date' => 'date', 'end_date' => 'date', - 'repeats' => 'boolean', ]; + protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount']; /** * @param string $value @@ -68,20 +77,10 @@ class BudgetLimit extends Model /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ - public function budget() + public function budget(): BelongsTo { return $this->belongsTo(Budget::class); } - - /** - * @codeCoverageIgnore - * - * @param $value - */ - public function setAmountAttribute($value) - { - $this->attributes['amount'] = (string)round($value, 12); - } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 2e2f0aaf26..5f06f91fbe 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -23,16 +23,17 @@ declare(strict_types=1); namespace FireflyIII\Models; use Crypt; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\User; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionJournal; /** * Class Category. + * + * @property string $name + * @property int $id */ class Category extends Model { diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 6a2b97c9bf..8f29c16dc0 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -27,6 +27,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; /** * Class Configuration. + * + * @property string $data + * @property string $name */ class Configuration extends Model { diff --git a/app/Models/CurrencyExchangeRate.php b/app/Models/CurrencyExchangeRate.php index 7701cb9fff..e77bb2484c 100644 --- a/app/Models/CurrencyExchangeRate.php +++ b/app/Models/CurrencyExchangeRate.php @@ -22,12 +22,24 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * Class CurrencyExchange. + * + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property TransactionCurrency $fromCurrency + * @property TransactionCurrency $toCurrency + * @property float $rate + * @property Carbon $date + * @property int $from_currency_id + * @property int $to_currency_id + * */ class CurrencyExchangeRate extends Model { diff --git a/app/Models/LinkType.php b/app/Models/LinkType.php index 04dd71f3aa..b9caaf05b7 100644 --- a/app/Models/LinkType.php +++ b/app/Models/LinkType.php @@ -22,15 +22,26 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** - * @property int $journalCount + * @property int $journalCount + * @property string $inward + * @property string $outward + * @property string $name + * @property bool $editable + * @property Carbon $created_at + * @property Carbon $updated_at + * @property int $id * Class LinkType + * */ class LinkType extends Model { + use SoftDeletes; /** * The attributes that should be casted to native types. * diff --git a/app/Models/Note.php b/app/Models/Note.php index a6588c8564..81ebc4a9ff 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -22,13 +22,22 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; /** * Class Note. + * + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $text + * @property string $title */ class Note extends Model { + use SoftDeletes; /** * The attributes that should be casted to native types. * diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 4e13b540c5..e6cc263d3e 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -29,17 +29,21 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Steam; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\PiggyBankRepetition; -use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\Note; -use FireflyIII\Models\Account; /** * Class PiggyBank. * - * @property Carbon $targetdate - * @property Carbon $startdate - * @property string $targetamount + * @property Carbon $targetdate + * @property Carbon $startdate + * @property string $targetamount + * @property int $id + * @property string $name + * @property Account $account + * @property Carbon $updated_at + * @property Carbon $created_at + * @property int $order + * @property bool $active + * @property int $account_id * */ class PiggyBank extends Model diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 9463fcc39c..0f9f364766 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\PiggyBank; /** * Class PiggyBankEvent. diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 3efac08652..470d9b7a11 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -25,11 +25,13 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; -use FireflyIII\Models\PiggyBank; /** * Class PiggyBankRepetition. + * * @property string $currentamount + * @property Carbon $startdate + * @property Carbon $targetdate */ class PiggyBankRepetition extends Model { diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 3a873f7086..19a337bb5c 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -22,19 +22,24 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Crypt; use Exception; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\User; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; use Log; -use FireflyIII\User; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Preference. * - * @property mixed $data + * @property mixed $data * @property string $name + * @property Carbon $updated_at + * @property Carbon $created_at + * @property int $id */ class Preference extends Model { @@ -52,6 +57,25 @@ class Preference extends Model /** @var array */ protected $fillable = ['user_id', 'data', 'name']; + /** + * @param string $value + * + * @return Account + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public static function routeBinder(string $value): Preference + { + if (auth()->check()) { + $preferenceId = (int)$value; + $preference = auth()->user()->preferences()->find($preferenceId); + if (null !== $preference) { + return $preference; + } + } + throw new NotFoundHttpException; + } + + /** * @param $value * diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php new file mode 100644 index 0000000000..bc60c3bad2 --- /dev/null +++ b/app/Models/Recurrence.php @@ -0,0 +1,171 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Models; + + +use FireflyIII\User; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * Class Recurrence + * + * @property int $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property int $user_id + * @property int $transaction_type_id + * @property int $transaction_currency_id + * @property string $title + * @property string $description + * @property \Carbon\Carbon $first_date + * @property \Carbon\Carbon $repeat_until + * @property \Carbon\Carbon $latest_date + * @property string $repetition_type + * @property string $repetition_moment + * @property int $repetition_skip + * @property int $repetitions + * @property bool $active + * @property bool $apply_rules + * @property \FireflyIII\User $user + * @property \Illuminate\Support\Collection $recurrenceRepetitions + * @property \Illuminate\Support\Collection $recurrenceMeta + * @property \Illuminate\Support\Collection $recurrenceTransactions + * @property \FireflyIII\Models\TransactionType $transactionType + * + */ +class Recurrence extends Model +{ + use SoftDeletes; + /** + * The attributes that should be casted to native types. + * + * @var array + */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'title' => 'string', + 'id' => 'int', + 'description' => 'string', + 'first_date' => 'date', + 'repeat_until' => 'date', + 'latest_date' => 'date', + 'repetitions' => 'int', + 'active' => 'bool', + 'apply_rules' => 'bool', + ]; + /** @var array */ + protected $fillable + = ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active']; + /** @var string */ + protected $table = 'recurrences'; + + /** + * @param string $value + * + * @return Recurrence + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public static function routeBinder(string $value): Recurrence + { + if (auth()->check()) { + $recurrenceId = (int)$value; + $recurrence = auth()->user()->recurrences()->find($recurrenceId); + if (null !== $recurrence) { + return $recurrence; + } + } + throw new NotFoundHttpException; + } + + /** + * @codeCoverageIgnore + * Get all of the notes. + */ + public function notes() + { + return $this->morphMany(Note::class, 'noteable'); + } + + /** + * @return HasMany + * @codeCoverageIgnore + */ + public function recurrenceMeta(): HasMany + { + return $this->hasMany(RecurrenceMeta::class); + } + + /** + * @return HasMany + * @codeCoverageIgnore + */ + public function recurrenceRepetitions(): HasMany + { + return $this->hasMany(RecurrenceRepetition::class); + } + + /** + * @return HasMany + * @codeCoverageIgnore + */ + public function recurrenceTransactions(): HasMany + { + return $this->hasMany(RecurrenceTransaction::class); + } + + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function transactionCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } + + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function transactionType(): BelongsTo + { + return $this->belongsTo(TransactionType::class); + } + + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + +} \ No newline at end of file diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php new file mode 100644 index 0000000000..97dc354c87 --- /dev/null +++ b/app/Models/RecurrenceMeta.php @@ -0,0 +1,63 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Models; + + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; + +/** + * Class RecurrenceMeta + * + * @property string $name + * @property string $value + */ +class RecurrenceMeta extends Model +{ + use SoftDeletes; + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'name' => 'string', + 'value' => 'string', + ]; + /** @var array */ + protected $fillable = ['recurrence_id', 'name', 'value']; + /** @var string */ + protected $table = 'recurrences_meta'; + + /** + * @return BelongsTo + * @codeCoverageIgnore + */ + public function recurrence(): BelongsTo + { + return $this->belongsTo(Recurrence::class); + } + +} \ No newline at end of file diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php new file mode 100644 index 0000000000..015a777a0e --- /dev/null +++ b/app/Models/RecurrenceRepetition.php @@ -0,0 +1,77 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Models; + + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; + +/** + * Class RecurrenceRepetition + * + * @property string $repetition_type + * @property string $repetition_moment + * @property int $repetition_skip + * @property int $weekend + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $deleted_at + * @property \Carbon\Carbon $updated_at + * @property int $id + */ +class RecurrenceRepetition extends Model +{ + /** @var int */ + public const WEEKEND_DO_NOTHING = 1; + /** @var int */ + public const WEEKEND_SKIP_CREATION = 2; + /** @var int */ + public const WEEKEND_TO_FRIDAY = 3; + /** @var int */ + public const WEEKEND_TO_MONDAY = 4; + use SoftDeletes; + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'repetition_type' => 'string', + 'repetition_moment' => 'string', + 'repetition_skip' => 'int', + 'weekend' => 'int', + ]; + protected $fillable = ['recurrence_id', 'weekend', 'repetition_type', 'repetition_moment', 'repetition_skip']; + /** @var string */ + protected $table = 'recurrences_repetitions'; + + /** + * @return BelongsTo + * @codeCoverageIgnore + */ + public function recurrence(): BelongsTo + { + return $this->belongsTo(Recurrence::class); + } +} \ No newline at end of file diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php new file mode 100644 index 0000000000..bea0b38ff6 --- /dev/null +++ b/app/Models/RecurrenceTransaction.php @@ -0,0 +1,123 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Models; + + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; + +/** + * + * Class RecurrenceTransaction + * + * @property int $transaction_currency_id, + * @property int $foreign_currency_id + * @property int $source_id + * @property int $destination_id + * @property string $amount + * @property string $foreign_amount + * @property string $description + * @property \FireflyIII\Models\TransactionCurrency $transactionCurrency + * @property \FireflyIII\Models\TransactionCurrency $foreignCurrency + * @property \FireflyIII\Models\Account $sourceAccount + * @property \FireflyIII\Models\Account $destinationAccount + * @property \Illuminate\Support\Collection $recurrenceTransactionMeta + * @property int $id + */ +class RecurrenceTransaction extends Model +{ + use SoftDeletes; + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'amount' => 'string', + 'foreign_amount' => 'string', + 'description' => 'string', + ]; + /** @var array */ + protected $fillable + = ['recurrence_id', 'transaction_currency_id', 'foreign_currency_id', 'source_id', 'destination_id', 'amount', 'foreign_amount', + 'description']; + /** @var string */ + protected $table = 'recurrences_transactions'; + + /** + * @codeCoverageIgnore + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function destinationAccount(): BelongsTo + { + return $this->belongsTo(Account::class,'destination_id'); + } + + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function foreignCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } + + /** + * @return BelongsTo + * @codeCoverageIgnore + */ + public function recurrence(): BelongsTo + { + return $this->belongsTo(Recurrence::class); + } + + /** + * @return HasMany + * @codeCoverageIgnore + */ + public function recurrenceTransactionMeta(): HasMany + { + return $this->hasMany(RecurrenceTransactionMeta::class, 'rt_id'); + } + + /** + * @codeCoverageIgnore + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function sourceAccount(): BelongsTo + { + return $this->belongsTo(Account::class,'source_id'); + } + + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function transactionCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } +} \ No newline at end of file diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php new file mode 100644 index 0000000000..aa246529cf --- /dev/null +++ b/app/Models/RecurrenceTransactionMeta.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Models; + + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; + +/** + * Class RecurrenceTransactionMeta + * + * @property string $name + * @property string $value + */ +class RecurrenceTransactionMeta extends Model +{ + use SoftDeletes; + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'name' => 'string', + 'value' => 'string', + ]; + protected $fillable = ['rt_id', 'name', 'value']; + /** @var string */ + protected $table = 'rt_meta'; + + /** + * @return BelongsTo + * @codeCoverageIgnore + */ + public function recurrenceTransaction(): BelongsTo + { + return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); + } + +} \ No newline at end of file diff --git a/app/Models/Role.php b/app/Models/Role.php index 02fcfb5a5d..ee735d4605 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -22,9 +22,9 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use FireflyIII\User; /** * Class Role. diff --git a/app/Models/Rule.php b/app/Models/Rule.php index e6e65ba73e..36982b40c3 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -22,19 +22,33 @@ declare(strict_types=1); namespace FireflyIII\Models; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\SoftDeletes; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Carbon\Carbon; use FireflyIII\User; -use FireflyIII\Models\RuleTrigger; -use FireflyIII\Models\RuleGroup; -use FireflyIII\Models\RuleAction; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class Rule. - * @property bool $stop_processing - * @property int $id - * @property \Illuminate\Support\Collection $ruleTriggers + * + * @property bool $stop_processing + * @property int $id + * @property Collection $ruleTriggers + * @property Collection $ruleActions + * @property bool $active + * @property bool $strict + * @property User $user + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $title + * @property string $text + * @property int $order + * @property RuleGroup $ruleGroup + * @property int $rule_group_id + * @property string $description */ class Rule extends Model { @@ -53,10 +67,11 @@ class Rule extends Model 'active' => 'boolean', 'order' => 'int', 'stop_processing' => 'boolean', + 'id' => 'int', 'strict' => 'boolean', ]; /** @var array */ - protected $fillable = ['rule_group_id', 'order', 'active', 'title', 'description', 'user_id','strict']; + protected $fillable = ['rule_group_id', 'order', 'active', 'title', 'description', 'user_id', 'strict']; /** * @param string $value @@ -78,27 +93,27 @@ class Rule extends Model /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return HasMany */ - public function ruleActions() + public function ruleActions(): HasMany { return $this->hasMany(RuleAction::class); } /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ - public function ruleGroup() + public function ruleGroup(): BelongsTo { return $this->belongsTo(RuleGroup::class); } /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return HasMany */ - public function ruleTriggers() + public function ruleTriggers(): HasMany { return $this->hasMany(RuleTrigger::class); } @@ -106,16 +121,16 @@ class Rule extends Model /** * @param $value */ - public function setDescriptionAttribute($value) + public function setDescriptionAttribute($value): void { $this->attributes['description'] = e($value); } /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ - public function user() + public function user(): BelongsTo { return $this->belongsTo(User::class); } diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index d498ee11f6..6bad677108 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -22,11 +22,21 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; -use FireflyIII\Models\Rule; /** * Class RuleAction. + * + * @property string $action_value + * @property string $action_type + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property int $order + * @property bool $active + * @property bool $stop_processing + * @property Rule $rule */ class RuleAction extends Model { diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index cfe4ae2c35..142ec71bda 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -22,15 +22,25 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\User; -use FireflyIII\Models\Rule; /** * Class RuleGroup. - * @property bool $active + * + * @property bool $active + * @property User $user + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $title + * @property string $text + * @property int $id + * @property int $order + * @property Collection $rules */ class RuleGroup extends Model { diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 58265d0d1c..b030f9d836 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; /** @@ -29,6 +30,12 @@ use Illuminate\Database\Eloquent\Model; * * @property string $trigger_value * @property string $trigger_type + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property int $order + * @property bool $active + * @property bool $stop_processing */ class RuleTrigger extends Model { diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 9a085b0325..a60b07449b 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -23,19 +23,18 @@ declare(strict_types=1); namespace FireflyIII\Models; use Crypt; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\User; -use FireflyIII\Models\TransactionJournal; /** * Class Tag. * - * @property Collection $transactionJournals - * @property string $tag - * @property int $id + * @property Collection $transactionJournals + * @property string $tag + * @property int $id * @property \Carbon\Carbon $date */ class Tag extends Model diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index b591f1d956..5d4721becb 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -25,6 +25,7 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -71,7 +72,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property string $description * @property bool $is_split * @property int $attachmentCount - * @property int $transaction_currency_id + * @property int $transaction_currency_id + * @property int $foreign_currency_id + * @property string $amount + * @property string $foreign_amount + * @property TransactionJournal $transactionJournal + * @property Account $account */ class Transaction extends Model { @@ -252,18 +258,18 @@ class Transaction extends Model /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ - public function transactionCurrency() + public function transactionCurrency(): BelongsTo { return $this->belongsTo(TransactionCurrency::class); } /** * @codeCoverageIgnore - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return BelongsTo */ - public function transactionJournal() + public function transactionJournal(): BelongsTo { return $this->belongsTo(TransactionJournal::class); } diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 439962a257..26e7d9fa0c 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -25,13 +25,14 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\TransactionJournal; /** * Class TransactionCurrency. * * @property string $code - * @property int $decimal_places + * @property string $symbol + * @property int $decimal_places + * @property int $id * */ class TransactionCurrency extends Model diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 40595f644d..e6be30af6a 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -32,26 +32,20 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; use Log; use Preferences; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionType; -use FireflyIII\Models\TransactionJournalMeta; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\Tag; -use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\Note; -use FireflyIII\Models\Category; -use FireflyIII\Models\Budget; -use FireflyIII\Models\Bill; -use FireflyIII\Models\Attachment; /** * Class TransactionJournal. * - * @property User $user - * @property int $bill_id + * @property User $user + * @property int $bill_id + * @property Collection $categories + * @property bool $completed + * @property string $description + * @property string $transaction_type_id */ class TransactionJournal extends Model { diff --git a/app/Models/TransactionJournalLink.php b/app/Models/TransactionJournalLink.php index af47153390..aa5146b3e7 100644 --- a/app/Models/TransactionJournalLink.php +++ b/app/Models/TransactionJournalLink.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Models; +use Carbon\Carbon; use Crypt; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -29,6 +30,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class TransactionJournalLink. + * + * @property int $id + * @property Carbon $created_at + * @property Carbon $updated_at + * @property string $comment + * @property TransactionJournal $source + * @property TransactionJournal $destination + * @property LinkType $linkType + * @property int $link_type_id + * @property int $source_id + * @property int $destination_id */ class TransactionJournalLink extends Model { diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index bef13e7fa8..e6acea708f 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -25,12 +25,12 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; -use FireflyIII\Models\TransactionJournal; /** * Class TransactionJournalMeta. + * * @property string $name - * @property int $transaction_journal_id + * @property int $transaction_journal_id */ class TransactionJournalMeta extends Model { diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 67a1b906e1..4c9bc1f04a 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -25,11 +25,12 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\TransactionJournal; /** * Class TransactionType. + * * @property string $type + * @property int $id */ class TransactionType extends Model { diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 352e357d22..2ffb23d88b 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -26,6 +26,7 @@ use Exception; use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\RequestedNewPassword; +use FireflyIII\Events\RequestedReportOnJournals; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Events\StoredTransactionJournal; use FireflyIII\Events\UpdatedTransactionJournal; @@ -64,11 +65,15 @@ class EventServiceProvider extends ServiceProvider // is a User related event. Login::class => [ 'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin', + 'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish', ], RequestedVersionCheckStatus::class => [ 'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates', ], + RequestedReportOnJournals::class => [ + 'FireflyIII\Handlers\Events\AutomationHandler@reportJournals', + ], // is a User related event. RequestedNewPassword::class => [ @@ -112,7 +117,7 @@ class EventServiceProvider extends ServiceProvider */ protected function registerCreateEvents(): void { - // move this routine to a filter + // todo move this routine to a filter // in case of repeated piggy banks and/or other problems. PiggyBank::created( function (PiggyBank $piggyBank) { diff --git a/app/Providers/RecurringServiceProvider.php b/app/Providers/RecurringServiceProvider.php new file mode 100644 index 0000000000..d41f54e743 --- /dev/null +++ b/app/Providers/RecurringServiceProvider.php @@ -0,0 +1,63 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Providers; + +use FireflyIII\Repositories\Recurring\RecurringRepository; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use Illuminate\Foundation\Application; +use Illuminate\Support\ServiceProvider; + +/** + * @codeCoverageIgnore + * Class RecurringServiceProvider. + */ +class RecurringServiceProvider extends ServiceProvider +{ + /** + * Bootstrap the application services. + */ + public function boot(): void + { + } + + /** + * Register the application services. + */ + public function register(): void + { + $this->app->bind( + RecurringRepositoryInterface::class, + function (Application $app) { + /** @var RecurringRepositoryInterface $repository */ + $repository = app(RecurringRepository::class); + + if ($app->auth->check()) { + $repository->setUser(auth()->user()); + } + + return $repository; + } + ); + } + +} diff --git a/app/Repositories/Account/FindAccountsTrait.php b/app/Repositories/Account/FindAccountsTrait.php index 79451ff6e4..8cfadbe8dc 100644 --- a/app/Repositories/Account/FindAccountsTrait.php +++ b/app/Repositories/Account/FindAccountsTrait.php @@ -58,6 +58,7 @@ trait FindAccountsTrait /** * @param string $number * @param array $types + * * @return Account|null */ public function findByAccountNumber(string $number, array $types): ?Account @@ -157,9 +158,11 @@ trait FindAccountsTrait Log::debug(sprintf('Found #%d (%s) with type id %d', $account->id, $account->name, $account->account_type_id)); return $account; + } else { + Log::debug(sprintf('"%s" does not equal "%s"', $account->name, $name)); } } - Log::debug(sprintf('There is no account with name "%s" or types', $name), $types); + Log::debug(sprintf('There is no account with name "%s" of types', $name), $types); return null; } diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 36d38ade9d..d5eac20346 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -25,6 +25,8 @@ namespace FireflyIII\Repositories\Attachment; use Carbon\Carbon; use Crypt; use Exception; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\AttachmentFactory; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Models\Attachment; use FireflyIII\Models\Note; @@ -179,11 +181,30 @@ class AttachmentRepository implements AttachmentRepositoryInterface /** * @param User $user */ - public function setUser(User $user) + public function setUser(User $user): void { $this->user = $user; } + /** + * @param array $data + * + * @return Attachment + * @throws FireflyException + */ + public function store(array $data): Attachment + { + /** @var AttachmentFactory $factory */ + $factory = app(AttachmentFactory::class); + $factory->setUser($this->user); + $result = $factory->create($data); + if (null === $result) { + throw new FireflyException('Could not store attachment.'); + } + + return $result; + } + /** * @param Attachment $attachment * @param array $data @@ -193,8 +214,13 @@ class AttachmentRepository implements AttachmentRepositoryInterface public function update(Attachment $attachment, array $data): Attachment { $attachment->title = $data['title']; + + // update filename, if present and different: + if (isset($data['filename']) && '' !== $data['filename'] && $data['filename'] !== $attachment->filename) { + $attachment->filename = $data['filename']; + } $attachment->save(); - $this->updateNote($attachment, $data['notes']); + $this->updateNote($attachment, $data['notes'] ?? ''); return $attachment; } @@ -207,7 +233,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface */ public function updateNote(Attachment $attachment, string $note): bool { - if (0 === \strlen($note)) { + if ('' === $note) { $dbNote = $attachment->notes()->first(); if (null !== $dbNote) { $dbNote->delete(); diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index 0a2b63b537..30caf2c253 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Attachment; use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Attachment; use FireflyIII\User; use Illuminate\Support\Collection; @@ -95,6 +96,14 @@ interface AttachmentRepositoryInterface */ public function setUser(User $user); + /** + * @param array $data + * + * @return Attachment + * @throws FireflyException + */ + public function store(array $data): Attachment; + /** * @param Attachment $attachment * @param array $attachmentData diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index c0ee968441..443a117405 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; +use Exception; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\AvailableBudget; @@ -152,19 +154,48 @@ class BudgetRepository implements BudgetRepositoryInterface return $return; } + /** + * Deletes a budget limit. + * + * @param BudgetLimit $budgetLimit + */ + public function deleteBudgetLimit(BudgetLimit $budgetLimit): void + { + try { + $budgetLimit->delete(); + } catch (Exception $e) { + Log::error(sprintf('Could not delete budget limit: %s', $e->getMessage())); + } + } + /** * @param Budget $budget * * @return bool - * @throws \Exception */ public function destroy(Budget $budget): bool { - $budget->delete(); + try { + $budget->delete(); + } catch (Exception $e) { + Log::error(sprintf('Could not delete budget: %s', $e->getMessage())); + } return true; } + /** + * @param AvailableBudget $availableBudget + */ + public function destroyAvailableBudget(AvailableBudget $availableBudget): void + { + try { + $availableBudget->delete(); + } catch (Exception $e) { + Log::error(sprintf('Could not delete available budget: %s', $e->getMessage())); + } + } + /** * Filters entries from the result set generated by getBudgetPeriodReport. * @@ -298,8 +329,35 @@ class BudgetRepository implements BudgetRepositoryInterface * * @return Collection */ - public function getAllBudgetLimits(Carbon $start, Carbon $end): Collection + public function getAllBudgetLimits(Carbon $start = null, Carbon $end = null): Collection { + // both are NULL: + if (null === $start && null === $end) { + $set = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->with(['budget']) + ->where('budgets.user_id', $this->user->id) + ->get(['budget_limits.*']); + + return $set; + } + // one of the two is NULL. + if (null === $start xor null === $end) { + $query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') + ->with(['budget']) + ->where('budgets.user_id', $this->user->id); + if (null !== $end) { + // end date must be before $end. + $query->where('end_date', '<=', $end->format('Y-m-d 00:00:00')); + } + if (null !== $start) { + // start date must be after $start. + $query->where('start_date', '>=', $start->format('Y-m-d 00:00:00')); + } + $set = $query->get(['budget_limits.*']); + + return $set; + } + // neither are NULL: $set = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->with(['budget']) ->where('budgets.user_id', $this->user->id) @@ -355,6 +413,16 @@ class BudgetRepository implements BudgetRepositoryInterface return $amount; } + /** + * Returns all available budget objects. + * + * @return Collection + */ + public function getAvailableBudgets(): Collection + { + return $this->user->availableBudgets()->get(); + } + /** * @param Budget $budget * @param Carbon $start @@ -362,8 +430,28 @@ class BudgetRepository implements BudgetRepositoryInterface * * @return Collection */ - public function getBudgetLimits(Budget $budget, Carbon $start, Carbon $end): Collection + public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection { + if (null === $end && null === $start) { + return $budget->budgetlimits()->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']); + } + if (null === $end xor null === $start) { + $query = $budget->budgetlimits()->orderBy('budget_limits.start_date', 'DESC'); + // one of the two is null + if (null !== $end) { + // end date must be before $end. + $query->where('end_date', '<=', $end->format('Y-m-d 00:00:00')); + } + if (null !== $start) { + // start date must be after $start. + $query->where('start_date', '>=', $start->format('Y-m-d 00:00:00')); + } + $set = $query->get(['budget_limits.*']); + + return $set; + } + + // when both dates are set: $set = $budget->budgetlimits() ->where( function (Builder $q5) use ($start, $end) { @@ -527,9 +615,9 @@ class BudgetRepository implements BudgetRepositoryInterface * @param Carbon $end * @param string $amount * - * @return bool + * @return AvailableBudget */ - public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool + public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget { $availableBudget = $this->user->availableBudgets() ->where('transaction_currency_id', $currency->id) @@ -545,7 +633,7 @@ class BudgetRepository implements BudgetRepositoryInterface $availableBudget->amount = $amount; $availableBudget->save(); - return true; + return $availableBudget; } /** @@ -636,6 +724,42 @@ class BudgetRepository implements BudgetRepositoryInterface return $newBudget; } + /** + * @param array $data + * + * @throws FireflyException + * @return BudgetLimit + */ + public function storeBudgetLimit(array $data): BudgetLimit + { + $this->cleanupBudgets(); + /** @var Budget $budget */ + $budget = $data['budget']; + + // find limit with same date range. + // if it exists, throw error. + $limits = $budget->budgetlimits() + ->where('budget_limits.start_date', $data['start_date']->format('Y-m-d 00:00:00')) + ->where('budget_limits.end_date', $data['end_date']->format('Y-m-d 00:00:00')) + ->get(['budget_limits.*'])->count(); + Log::debug(sprintf('Found %d budget limits.', $limits)); + if ($limits > 0) { + throw new FireflyException('A budget limit for this budget, and this date range already exists. You must update the existing one.'); + } + + Log::debug('No existing budget limit, create a new one'); + // or create one and return it. + $limit = new BudgetLimit; + $limit->budget()->associate($budget); + $limit->start_date = $data['start_date']->format('Y-m-d 00:00:00'); + $limit->end_date = $data['end_date']->format('Y-m-d 00:00:00'); + $limit->amount = $data['amount']; + $limit->save(); + Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $data['amount'])); + + return $limit; + } + /** * @param Budget $budget * @param array $data @@ -652,6 +776,58 @@ class BudgetRepository implements BudgetRepositoryInterface return $budget; } + /** + * @param AvailableBudget $availableBudget + * @param array $data + * + * @return AvailableBudget + * @throws FireflyException + */ + public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget + { + $existing = $this->user->availableBudgets() + ->where('transaction_currency_id', $data['transaction_currency_id']) + ->where('start_date', $data['start_date']->format('Y-m-d 00:00:00')) + ->where('end_date', $data['end_date']->format('Y-m-d 00:00:00')) + ->where('id', '!=', $availableBudget->id) + ->first(); + + if (null !== $existing) { + throw new FireflyException(sprintf('An entry already exists for these parameters: available budget object with ID #%d', $existing->id)); + } + $availableBudget->transaction_currency_id = $data['transaction_currency_id']; + $availableBudget->start_date = $data['start_date']; + $availableBudget->end_date = $data['end_date']; + $availableBudget->amount = $data['amount']; + $availableBudget->save(); + + return $availableBudget; + + } + + /** + * @param BudgetLimit $budgetLimit + * @param array $data + * + * @return BudgetLimit + * @throws Exception + */ + public function updateBudgetLimit(BudgetLimit $budgetLimit, array $data): BudgetLimit + { + $this->cleanupBudgets(); + /** @var Budget $budget */ + $budget = $data['budget']; + + $budgetLimit->budget()->associate($budget); + $budgetLimit->start_date = $data['start_date']->format('Y-m-d 00:00:00'); + $budgetLimit->end_date = $data['end_date']->format('Y-m-d 00:00:00'); + $budgetLimit->amount = $data['amount']; + $budgetLimit->save(); + Log::debug(sprintf('Updated budget limit with ID #%d and amount %s', $budgetLimit->id, $data['amount'])); + + return $budgetLimit; + } + /** * @param Budget $budget * @param Carbon $start diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index a929e92484..d8e69f2fef 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; +use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; @@ -35,15 +36,6 @@ use Illuminate\Support\Collection; interface BudgetRepositoryInterface { - /** - * Get all budgets with these ID's. - * - * @param array $budgetIds - * - * @return Collection - */ - public function getByIds(array $budgetIds): Collection; - /** * A method that returns the amount of money budgeted per day for this budget, * on average. @@ -71,6 +63,13 @@ interface BudgetRepositoryInterface */ public function collectBudgetInformation(Collection $budgets, Carbon $start, Carbon $end): array; + /** + * Deletes a budget limit. + * + * @param BudgetLimit $budgetLimit + */ + public function deleteBudgetLimit(BudgetLimit $budgetLimit): void; + /** * @param Budget $budget * @@ -78,6 +77,11 @@ interface BudgetRepositoryInterface */ public function destroy(Budget $budget): bool; + /** + * @param AvailableBudget $availableBudget + */ + public function destroyAvailableBudget(AvailableBudget $availableBudget): void; + /** * Filters entries from the result set generated by getBudgetPeriodReport. * @@ -139,7 +143,7 @@ interface BudgetRepositoryInterface * * @return Collection */ - public function getAllBudgetLimits(Carbon $start, Carbon $end): Collection; + public function getAllBudgetLimits(Carbon $start = null, Carbon $end = null): Collection; /** * @param TransactionCurrency $currency @@ -150,6 +154,13 @@ interface BudgetRepositoryInterface */ public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string; + /** + * Returns all available budget objects. + * + * @return Collection + */ + public function getAvailableBudgets(): Collection; + /** * @param Budget $budget * @param Carbon $start @@ -157,7 +168,7 @@ interface BudgetRepositoryInterface * * @return Collection */ - public function getBudgetLimits(Budget $budget, Carbon $start, Carbon $end): Collection; + public function getBudgetLimits(Budget $budget, Carbon $start = null, Carbon $end = null): Collection; /** * @param Collection $budgets @@ -174,6 +185,15 @@ interface BudgetRepositoryInterface */ public function getBudgets(): Collection; + /** + * Get all budgets with these ID's. + * + * @param array $budgetIds + * + * @return Collection + */ + public function getByIds(array $budgetIds): Collection; + /** * @return Collection */ @@ -194,9 +214,9 @@ interface BudgetRepositoryInterface * @param Carbon $end * @param string $amount * - * @return bool + * @return AvailableBudget */ - public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool; + public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget; /** * @param User $user @@ -213,6 +233,7 @@ interface BudgetRepositoryInterface */ public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param Collection $accounts * @param Carbon $start @@ -229,6 +250,13 @@ interface BudgetRepositoryInterface */ public function store(array $data): Budget; + /** + * @param array $data + * + * @return BudgetLimit + */ + public function storeBudgetLimit(array $data): BudgetLimit; + /** * @param Budget $budget * @param array $data @@ -237,6 +265,22 @@ interface BudgetRepositoryInterface */ public function update(Budget $budget, array $data): Budget; + /** + * @param AvailableBudget $availableBudget + * @param array $data + * + * @return AvailableBudget + */ + public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget; + + /** + * @param BudgetLimit $budgetLimit + * @param array $data + * + * @return BudgetLimit + */ + public function updateBudgetLimit(BudgetLimit $budgetLimit, array $data): BudgetLimit; + /** * @param Budget $budget * @param Carbon $start diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index 4258432124..f754c1bf0a 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -265,13 +265,15 @@ class CurrencyRepository implements CurrencyRepositoryInterface } /** + * Get currency exchange rate. + * * @param TransactionCurrency $fromCurrency * @param TransactionCurrency $toCurrency * @param Carbon $date * - * @return CurrencyExchangeRate + * @return CurrencyExchangeRate|null */ - public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate + public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): ?CurrencyExchangeRate { if ($fromCurrency->id === $toCurrency->id) { $rate = new CurrencyExchangeRate; @@ -280,7 +282,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $rate; } - + /** @var CurrencyExchangeRate $rate */ $rate = $this->user->currencyExchangeRates() ->where('from_currency_id', $fromCurrency->id) ->where('to_currency_id', $toCurrency->id) @@ -291,7 +293,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $rate; } - return new CurrencyExchangeRate; + return null; } /** diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 6f1be847d7..72f12a0188 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -154,13 +154,15 @@ interface CurrencyRepositoryInterface public function getCurrencyByPreference(Preference $preference): TransactionCurrency; /** + * Get currency exchange rate. + * * @param TransactionCurrency $fromCurrency * @param TransactionCurrency $toCurrency * @param Carbon $date * - * @return CurrencyExchangeRate + * @return CurrencyExchangeRate|null */ - public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate; + public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): ?CurrencyExchangeRate; /** * @param User $user diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 4d42698aec..7bf69d8a18 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -360,6 +360,7 @@ class ImportJobRepository implements ImportJobRepositoryInterface $newConfig = array_merge($currentConfig, $configuration); $job->configuration = $newConfig; $job->save(); + //Log::debug(sprintf('Set config of job "%s" to: ', $job->key), $newConfig); return $job; diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index baf1d51ce1..fd5ccd1d5e 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -36,52 +36,13 @@ use Symfony\Component\HttpFoundation\File\UploadedFile; interface ImportJobRepositoryInterface { /** - * Return all attachments for job. - * * @param ImportJob $job - * - * @return Collection - */ - public function getAttachments(ImportJob $job): Collection; - - /** - * Handle upload for job. - * - * @param ImportJob $job - * @param string $name - * @param UploadedFile $file - * - * @return MessageBag - * @throws FireflyException - */ - public function storeFileUpload(ImportJob $job, string $name, UploadedFile $file): MessageBag; - - /** - * Store file. - * - * @param ImportJob $job - * @param string $name - * @param string $fileName - * - * @return MessageBag - */ - public function storeCLIUpload(ImportJob $job, string $name, string $fileName): MessageBag; - - /** - * @param ImportJob $job - * @param array $transactions + * @param int $index + * @param string $error * * @return ImportJob */ - public function setTransactions(ImportJob $job, array $transactions): ImportJob; - - /** - * @param ImportJob $job - * @param Tag $tag - * - * @return ImportJob - */ - public function setTag(ImportJob $job, Tag $tag): ImportJob; + public function addError(ImportJob $job, int $index, string $error): ImportJob; /** * Add message to job. @@ -93,15 +54,6 @@ interface ImportJobRepositoryInterface */ public function addErrorMessage(ImportJob $job, string $error): ImportJob; - /** - * @param ImportJob $job - * @param int $index - * @param string $error - * - * @return ImportJob - */ - public function addError(ImportJob $job, int $index, string $error): ImportJob; - /** * @param ImportJob $job * @param int $steps @@ -141,6 +93,15 @@ interface ImportJobRepositoryInterface */ public function findByKey(string $key): ImportJob; + /** + * Return all attachments for job. + * + * @param ImportJob $job + * + * @return Collection + */ + public function getAttachments(ImportJob $job): Collection; + /** * Return configuration of job. * @@ -198,14 +159,6 @@ interface ImportJobRepositoryInterface */ public function setExtendedStatus(ImportJob $job, array $array): ImportJob; - /** - * @param ImportJob $job - * @param string $status - * - * @return ImportJob - */ - public function setStatus(ImportJob $job, string $status): ImportJob; - /** * @param ImportJob $job * @param string $stage @@ -214,6 +167,14 @@ interface ImportJobRepositoryInterface */ public function setStage(ImportJob $job, string $stage): ImportJob; + /** + * @param ImportJob $job + * @param string $status + * + * @return ImportJob + */ + public function setStatus(ImportJob $job, string $status): ImportJob; + /** * @param ImportJob $job * @param int $steps @@ -222,6 +183,14 @@ interface ImportJobRepositoryInterface */ public function setStepsDone(ImportJob $job, int $steps): ImportJob; + /** + * @param ImportJob $job + * @param Tag $tag + * + * @return ImportJob + */ + public function setTag(ImportJob $job, Tag $tag): ImportJob; + /** * @param ImportJob $job * @param int $count @@ -230,11 +199,42 @@ interface ImportJobRepositoryInterface */ public function setTotalSteps(ImportJob $job, int $count): ImportJob; + /** + * @param ImportJob $job + * @param array $transactions + * + * @return ImportJob + */ + public function setTransactions(ImportJob $job, array $transactions): ImportJob; + /** * @param User $user */ public function setUser(User $user); + /** + * Store file. + * + * @param ImportJob $job + * @param string $name + * @param string $fileName + * + * @return MessageBag + */ + public function storeCLIUpload(ImportJob $job, string $name, string $fileName): MessageBag; + + /** + * Handle upload for job. + * + * @param ImportJob $job + * @param string $name + * @param UploadedFile $file + * + * @return MessageBag + * @throws FireflyException + */ + public function storeFileUpload(ImportJob $job, string $name, UploadedFile $file): MessageBag; + /** * @param ImportJob $job * @param string $status diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index e5dc693a60..f3397e1d6b 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Journal; use Carbon\Carbon; -use DB; use Exception; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionJournalFactory; use FireflyIII\Factory\TransactionJournalMetaFactory; use FireflyIII\Models\Account; @@ -101,6 +101,11 @@ class JournalRepository implements JournalRepositoryInterface $transaction->budgets()->detach(); } } + // if journal is not a withdrawal, remove the bill ID. + if (TransactionType::WITHDRAWAL !== $type->type) { + $journal->bill_id = null; + $journal->save(); + } Preferences::mark(); @@ -739,8 +744,7 @@ class JournalRepository implements JournalRepositoryInterface * * @return TransactionJournal * - * @throws \FireflyIII\Exceptions\FireflyException - * @throws \FireflyIII\Exceptions\FireflyException + * @throws FireflyException */ public function store(array $data): TransactionJournal { diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index 33ea68806d..e0884a3858 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Journal; use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Models\Note; use FireflyIII\Models\Transaction; @@ -38,15 +39,6 @@ use Illuminate\Support\MessageBag; */ interface JournalRepositoryInterface { - /** - * Find a journal by its hash. - * - * @param string $hash - * - * @return TransactionJournalMeta|null - */ - public function findByHash(string $hash): ?TransactionJournalMeta; - /** * @param TransactionJournal $journal * @param TransactionType $type @@ -77,12 +69,22 @@ interface JournalRepositoryInterface * Find a specific journal. * * @param int $journalId + * * @deprecated * * @return TransactionJournal */ public function find(int $journalId): TransactionJournal; + /** + * Find a journal by its hash. + * + * @param string $hash + * + * @return TransactionJournalMeta|null + */ + public function findByHash(string $hash): ?TransactionJournalMeta; + /** * Find a specific journal. * @@ -325,6 +327,7 @@ interface JournalRepositoryInterface /** * @param array $data * + * @throws FireflyException * @return TransactionJournal */ public function store(array $data): TransactionJournal; diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index a4eb99cff6..a7c79b891a 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\LinkType; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\LinkType; use FireflyIII\Models\Note; @@ -56,9 +57,9 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface * @return bool * @throws \Exception */ - public function destroy(LinkType $linkType, LinkType $moveTo): bool + public function destroy(LinkType $linkType, LinkType $moveTo = null): bool { - if (null !== $moveTo->id) { + if (null !== $moveTo) { TransactionJournalLink::where('link_type_id', $linkType->id)->update(['link_type_id' => $moveTo->id]); } $linkType->delete(); @@ -94,6 +95,20 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $linkType; } + /** + * @param string|null $name + * + * @return LinkType|null + */ + public function findByName(string $name = null): ?LinkType + { + if (null === $name) { + return null; + } + + return LinkType::where('name', $name)->first(); + } + /** * Check if link exists between journals. * @@ -110,6 +125,34 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $count + $opposingCount > 0; } + /** + * @param int $id + * + * @return LinkType|null + */ + public function findNull(int $id): ?LinkType + { + return LinkType::find($id); + } + + /** + * See if such a link already exists (and get it). + * + * @param LinkType $linkType + * @param TransactionJournal $inward + * @param TransactionJournal $outward + * + * @return TransactionJournalLink|null + */ + public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink + { + return TransactionJournalLink + ::where('link_type_id', $linkType->id) + ->where('source_id', $inward->id) + ->where('destination_id', $outward->id)->first(); + + } + /** * @return Collection */ @@ -118,6 +161,30 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return LinkType::orderBy('name', 'ASC')->get(); } + /** + * Returns all the journal links (of a specific type). + * + * @param $linkType + * + * @return Collection + */ + public function getJournalLinks(LinkType $linkType = null): Collection + { + $query = TransactionJournalLink + ::leftJoin('transaction_journals as source_journals', 'journal_links.source_id', '=', 'source_journals.id') + ->leftJoin('transaction_journals as dest_journals', 'journal_links.destination_id', '=', 'dest_journals.id') + ->where('source_journals.user_id', $this->user->id) + ->where('dest_journals.user_id', $this->user->id) + ->whereNull('source_journals.deleted_at') + ->whereNull('dest_journals.deleted_at'); + + if (null !== $linkType) { + $query->where('journal_links.link_type_id', $linkType->id); + } + + return $query->get(['journal_links.*']); + } + /** * Return list of existing connections. * @@ -169,35 +236,42 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface * Store link between two journals. * * @param array $information - * @param TransactionJournal $left - * @param TransactionJournal $right + * @param TransactionJournal $inward + * @param TransactionJournal $outward * * @return mixed * @throws FireflyException */ - public function storeLink(array $information, TransactionJournal $left, TransactionJournal $right): TransactionJournalLink + public function storeLink(array $information, TransactionJournal $inward, TransactionJournal $outward): TransactionJournalLink { $linkType = $this->find((int)($information['link_type_id'] ?? 0)); if (null === $linkType->id) { throw new FireflyException(sprintf('Link type #%d cannot be resolved to an actual link type', $information['link_type_id'] ?? 0)); } + + // might exist already: + $existing = $this->findSpecificLink($linkType, $inward, $outward); + if (null !== $existing) { + return $existing; + } + $link = new TransactionJournalLink; $link->linkType()->associate($linkType); if ('inward' === $information['direction']) { - Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->inward, $left->id, $right->id)); - $link->source()->associate($left); - $link->destination()->associate($right); + Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->inward, $inward->id, $outward->id)); + $link->source()->associate($inward); + $link->destination()->associate($outward); } if ('outward' === $information['direction']) { - Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->outward, $right->id, $left->id)); - $link->source()->associate($right); - $link->destination()->associate($left); + Log::debug(sprintf('Link type is inwards ("%s"), so %d is source and %d is destination.', $linkType->outward, $outward->id, $inward->id)); + $link->source()->associate($outward); + $link->destination()->associate($inward); } $link->save(); // make note in noteable: - if (\strlen($information['notes']) > 0) { + if (\strlen((string)$information['notes']) > 0) { $dbNote = $link->notes()->first(); if (null === $dbNote) { $dbNote = new Note(); @@ -240,4 +314,42 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return $linkType; } + + /** + * Update an existing transaction journal link. + * + * @param TransactionJournalLink $journalLink + * @param array $data + * + * @return TransactionJournalLink + */ + public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink + { + $journalLink->source_id = $data['inward']->id; + $journalLink->destination_id = $data['outward']->id; + $journalLink->link_type_id = $data['link_type_id']; + $journalLink->save(); + /** @var Note $note */ + $note = $journalLink->notes()->first(); + // delete note: + if (null !== $note && '' === $data['notes']) { + try { + $note->delete(); + } catch (Exception $e) { + Log::debug(sprintf('Could not delete note for journal link: %s', $e->getMessage())); + } + } + // create note: + if (null === $note && '' !== $data['notes']) { + $note = new Note; + $note->noteable()->associate($journalLink); + } + // update note + if ('' !== $data['notes']) { + $note->text = $data['notes']; + $note->save(); + } + + return $journalLink; + } } diff --git a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php index 19ae1c8846..617cb45b59 100644 --- a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php +++ b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php @@ -45,7 +45,7 @@ interface LinkTypeRepositoryInterface * * @return bool */ - public function destroy(LinkType $linkType, LinkType $moveTo): bool; + public function destroy(LinkType $linkType, LinkType $moveTo = null): bool; /** * @param TransactionJournalLink $link @@ -57,10 +57,20 @@ interface LinkTypeRepositoryInterface /** * @param int $id * + * @deprecated * @return LinkType */ public function find(int $id): LinkType; + /** + * Find link type by name. + * + * @param string|null $name + * + * @return LinkType|null + */ + public function findByName(string $name = null): ?LinkType; + /** * Check if link exists between journals. * @@ -71,11 +81,36 @@ interface LinkTypeRepositoryInterface */ public function findLink(TransactionJournal $one, TransactionJournal $two): bool; + /** + * @param int $id + * + * @return LinkType|null + */ + public function findNull(int $id): ?LinkType; + + /** + * See if such a link already exists (and get it). + * + * @param LinkType $linkType + * @param TransactionJournal $inward + * @param TransactionJournal $outward + * + * @return TransactionJournalLink|null + */ + public function findSpecificLink(LinkType $linkType, TransactionJournal $inward, TransactionJournal $outward): ?TransactionJournalLink; + /** * @return Collection */ public function get(): Collection; + /** + * @param LinkType|null $linkType + * + * @return Collection + */ + public function getJournalLinks(LinkType $linkType = null): Collection; + /** * Return list of existing connections. * @@ -96,12 +131,12 @@ interface LinkTypeRepositoryInterface * Store link between two journals. * * @param array $information - * @param TransactionJournal $left - * @param TransactionJournal $right + * @param TransactionJournal $inward + * @param TransactionJournal $outward * * @return mixed */ - public function storeLink(array $information, TransactionJournal $left, TransactionJournal $right): TransactionJournalLink; + public function storeLink(array $information, TransactionJournal $inward, TransactionJournal $outward): TransactionJournalLink; /** * @param TransactionJournalLink $link @@ -117,4 +152,14 @@ interface LinkTypeRepositoryInterface * @return LinkType */ public function update(LinkType $linkType, array $data): LinkType; + + /** + * Update an existing transaction journal link. + * + * @param TransactionJournalLink $journalLink + * @param array $data + * + * @return TransactionJournalLink + */ + public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink; } diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 677b48c23e..cacf70be61 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -49,7 +49,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function addAmount(PiggyBank $piggyBank, string $amount): bool { - $repetition = $piggyBank->currentRelevantRep(); + $repetition = $this->getRepetition($piggyBank); + if (null === $repetition) { + return false; + } $currentAmount = $repetition->currentamount ?? '0'; $repetition->currentamount = bcadd($currentAmount, $amount); $repetition->save(); @@ -99,7 +102,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool { - $savedSoFar = $piggyBank->currentRelevantRep()->currentamount; + $repetition = $this->getRepetition($piggyBank); + if (null === $repetition) { + return false; + } + $savedSoFar = $repetition->currentamount; return bccomp($amount, $savedSoFar) <= 0; } @@ -171,6 +178,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * @param int $piggyBankid * + * @deprecated * @return PiggyBank */ public function find(int $piggyBankid): PiggyBank @@ -203,6 +211,21 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return null; } + /** + * @param int $piggyBankId + * + * @return PiggyBank|null + */ + public function findNull(int $piggyBankId): ?PiggyBank + { + $piggyBank = $this->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); + if (null !== $piggyBank) { + return $piggyBank; + } + + return null; + } + /** * Get current amount saved in piggy bank. * @@ -253,7 +276,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if piggy account matches source account, the amount is positive - if (\in_array($piggyBank->account_id, $sources)) { + if (\in_array($piggyBank->account_id, $sources, true)) { $amount = bcmul($amount, '-1'); Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id)); } @@ -334,8 +357,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string { $savePerMonth = '0'; - $repetition = $this->getRepetition($piggyBank); - if(null === $repetition) { + $repetition = $this->getRepetition($piggyBank); + if (null === $repetition) { return $savePerMonth; } if (null !== $piggyBank->targetdate && $repetition->currentamount < $piggyBank->targetamount) { @@ -423,8 +446,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * set id of piggy bank. * - * @param int $piggyBankId - * @param int $order + * @param PiggyBank $piggyBank + * @param int $order * * @return bool */ @@ -439,7 +462,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * @param User $user */ - public function setUser(User $user) + public function setUser(User $user): void { $this->user = $user; } @@ -455,7 +478,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** @var PiggyBank $piggyBank */ $piggyBank = PiggyBank::create($data); - $this->updateNote($piggyBank, $data['note']); + $this->updateNote($piggyBank, $data['note']); // todo rename to 'notes' + + // repetition is auto created. + $repetition = $this->getRepetition($piggyBank); + if (null !== $repetition && isset($data['current_amount'])) { + $repetition->currentamount = $data['current_amount']; + $repetition->save(); + } return $piggyBank; } @@ -470,9 +500,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface { $piggyBank->name = $data['name']; $piggyBank->account_id = (int)$data['account_id']; - $piggyBank->targetamount = round($data['targetamount'], 2); - $piggyBank->targetdate = $data['targetdate']; - $piggyBank->startdate = $data['startdate']; + $piggyBank->targetamount = $data['targetamount']; + $piggyBank->targetdate = $data['targetdate'] ?? $piggyBank->targetdate; + $piggyBank->startdate = $data['startdate'] ?? $piggyBank->startdate; $piggyBank->save(); @@ -480,7 +510,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface // if the piggy bank is now smaller than the current relevant rep, // remove money from the rep. - $repetition = $piggyBank->currentRelevantRep(); + $repetition = $this->getRepetition($piggyBank); if ($repetition->currentamount > $piggyBank->targetamount) { $diff = bcsub($piggyBank->targetamount, $repetition->currentamount); $this->createEvent($piggyBank, $diff); diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index 9b22a8d3b8..4b4d7d062e 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -103,6 +103,7 @@ interface PiggyBankRepositoryInterface /** * @param int $piggyBankid * + * @deprecated * @return PiggyBank */ public function find(int $piggyBankid): PiggyBank; @@ -116,6 +117,13 @@ interface PiggyBankRepositoryInterface */ public function findByName(string $name): ?PiggyBank; + /** + * @param int $piggyBankId + * + * @return PiggyBank|null + */ + public function findNull(int $piggyBankId): ?PiggyBank; + /** * Get current amount saved in piggy bank. * diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php new file mode 100644 index 0000000000..43be804321 --- /dev/null +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -0,0 +1,662 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Recurring; + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\RecurrenceFactory; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Helpers\Filter\InternalTransferFilter; +use FireflyIII\Models\Note; +use FireflyIII\Models\Preference; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionJournalMeta; +use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService; +use FireflyIII\Services\Internal\Update\RecurrenceUpdateService; +use FireflyIII\User; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use Log; + +/** + * + * Class RecurringRepository + */ +class RecurringRepository implements RecurringRepositoryInterface +{ + /** @var User */ + private $user; + + /** + * Destroy a recurring transaction. + * + * @param Recurrence $recurrence + */ + public function destroy(Recurrence $recurrence): void + { + /** @var RecurrenceDestroyService $service */ + $service = app(RecurrenceDestroyService::class); + $service->destroy($recurrence); + } + + /** + * Returns all of the user's recurring transactions. + * + * @return Collection + */ + public function get(): Collection + { + return $this->user->recurrences() + ->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) + ->orderBy('active', 'DESC') + ->orderBy('transaction_type_id', 'ASC') + ->orderBy('title', 'ASC') + ->get(); + } + + /** + * Get ALL recurring transactions. + * + * @return Collection + */ + public function getAll(): Collection + { + // grab ALL recurring transactions: + return Recurrence + ::with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) + ->orderBy('active', 'DESC') + ->orderBy('title', 'ASC') + ->get(); + } + + /** + * Get the budget ID from a recurring transaction transaction. + * + * @param RecurrenceTransaction $recurrenceTransaction + * + * @return null|int + */ + public function getBudget(RecurrenceTransaction $recurrenceTransaction): ?int + { + $return = 0; + /** @var RecurrenceTransactionMeta $meta */ + foreach ($recurrenceTransaction->recurrenceTransactionMeta as $meta) { + if ($meta->name === 'budget_id') { + $return = (int)$meta->value; + } + } + + return $return === 0 ? null : $return; + } + + /** + * Get the category from a recurring transaction transaction. + * + * @param RecurrenceTransaction $recurrenceTransaction + * + * @return null|string + */ + public function getCategory(RecurrenceTransaction $recurrenceTransaction): ?string + { + $return = ''; + /** @var RecurrenceTransactionMeta $meta */ + foreach ($recurrenceTransaction->recurrenceTransactionMeta as $meta) { + if ($meta->name === 'category_name') { + $return = (string)$meta->value; + } + } + + return $return === '' ? null : $return; + } + + /** + * Returns the journals created for this recurrence, possibly limited by time. + * + * @param Recurrence $recurrence + * @param Carbon|null $start + * @param Carbon|null $end + * + * @return Collection + */ + public function getJournals(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): Collection + { + $query = TransactionJournal + ::leftJoin('journal_meta', 'journal_meta.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.user_id', $recurrence->user_id) + ->whereNull('transaction_journals.deleted_at') + ->where('journal_meta.name', 'recurrence_id') + ->where('journal_meta.data', '"' . $recurrence->id . '"'); + if (null !== $start) { + $query->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')); + } + if (null !== $end) { + $query->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')); + } + $result = $query->get(['transaction_journals.*']); + + return $result; + } + + /** + * Get the notes. + * + * @param Recurrence $recurrence + * + * @return string + */ + public function getNoteText(Recurrence $recurrence): string + { + /** @var Note $note */ + $note = $recurrence->notes()->first(); + if (null !== $note) { + return (string)$note->text; + } + + return ''; + } + + /** + * Generate events in the date range. + * + * @param RecurrenceRepetition $repetition + * @param Carbon $start + * @param Carbon $end + * + * @throws FireflyException + * + * @return array + */ + public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array + { + $return = []; + $mutator = clone $start; + $mutator->startOfDay(); + $skipMod = $repetition->repetition_skip + 1; + $attempts = 0; + Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type)); + Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); + switch ($repetition->repetition_type) { + default: + throw new FireflyException( + sprintf('Cannot calculate occurrences for recurring transaction repetition type "%s"', $repetition->repetition_type) + ); + case 'daily': + Log::debug('Rep is daily. Start of loop.'); + while ($mutator <= $end) { + Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); + if ($attempts % $skipMod === 0) { + Log::debug(sprintf('Attempts modulo skipmod is zero, include %s', $mutator->format('Y-m-d'))); + $return[] = clone $mutator; + } + $mutator->addDay(); + $attempts++; + } + break; + case 'weekly': + Log::debug('Rep is weekly.'); + // monday = 1 + // sunday = 7 + $dayOfWeek = (int)$repetition->repetition_moment; + Log::debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $mutator->dayOfWeekIso)); + if ($mutator->dayOfWeekIso > $dayOfWeek) { + // day has already passed this week, add one week: + $mutator->addWeek(); + Log::debug(sprintf('Jump to next week, so mutator is now: %s', $mutator->format('Y-m-d'))); + } + // today is wednesday (3), expected is friday (5): add two days. + // today is friday (5), expected is monday (1), subtract four days. + Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); + $dayDifference = $dayOfWeek - $mutator->dayOfWeekIso; + $mutator->addDays($dayDifference); + Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); + while ($mutator <= $end) { + if ($attempts % $skipMod === 0 && $start->lte($mutator) && $end->gte($mutator)) { + Log::debug('Date is in range of start+end, add to set.'); + $return[] = clone $mutator; + } + $attempts++; + $mutator->addWeek(); + Log::debug(sprintf('Mutator is now (end of loop): %s', $mutator->format('Y-m-d'))); + } + break; + case 'monthly': + $dayOfMonth = (int)$repetition->repetition_moment; + Log::debug(sprintf('Day of month in repetition is %d', $dayOfMonth)); + Log::debug(sprintf('Start is %s.', $start->format('Y-m-d'))); + Log::debug(sprintf('End is %s.', $end->format('Y-m-d'))); + if ($mutator->day > $dayOfMonth) { + Log::debug('Add a month.'); + // day has passed already, add a month. + $mutator->addMonth(); + } + Log::debug(sprintf('Start is now %s.', $mutator->format('Y-m-d'))); + Log::debug('Start loop.'); + while ($mutator < $end) { + Log::debug(sprintf('Mutator is now %s.', $mutator->format('Y-m-d'))); + $domCorrected = min($dayOfMonth, $mutator->daysInMonth); + Log::debug(sprintf('DoM corrected is %d', $domCorrected)); + $mutator->day = $domCorrected; + Log::debug(sprintf('Mutator is now %s.', $mutator->format('Y-m-d'))); + Log::debug(sprintf('$attempts %% $skipMod === 0 is %s', var_export($attempts % $skipMod === 0, true))); + Log::debug(sprintf('$start->lte($mutator) is %s', var_export($start->lte($mutator), true))); + Log::debug(sprintf('$end->gte($mutator) is %s', var_export($end->gte($mutator), true))); + if ($attempts % $skipMod === 0 && $start->lte($mutator) && $end->gte($mutator)) { + Log::debug(sprintf('ADD %s to return!', $mutator->format('Y-m-d'))); + $return[] = clone $mutator; + } + $attempts++; + $mutator->endOfMonth()->startOfDay()->addDay(); + } + break; + case 'ndom': + $mutator->startOfMonth(); + // this feels a bit like a cop out but why reinvent the wheel? + $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',]; + $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',]; + $parts = explode(',', $repetition->repetition_moment); + while ($mutator <= $end) { + $string = sprintf('%s %s of %s %s', $counters[$parts[0]], $daysOfWeek[$parts[1]], $mutator->format('F'), $mutator->format('Y')); + $newCarbon = new Carbon($string); + $return[] = clone $newCarbon; + $mutator->endOfMonth()->addDay(); + } + break; + case 'yearly': + $date = new Carbon($repetition->repetition_moment); + $date->year = $mutator->year; + if ($mutator > $date) { + $date->addYear(); + + } + + // is $date between $start and $end? + $obj = clone $date; + $count = 0; + while ($obj <= $end && $obj >= $mutator && $count < 10) { + + $return[] = clone $obj; + $obj->addYears(1); + $count++; + } + break; + } + // filter out all the weekend days: + $return = $this->filterWeekends($repetition, $return); + + return $return; + } + + /** + * Get the tags from the recurring transaction. + * + * @param Recurrence $recurrence + * + * @return array + */ + public function getTags(Recurrence $recurrence): array + { + $tags = []; + /** @var RecurrenceMeta $meta */ + foreach ($recurrence->recurrenceMeta as $meta) { + if ($meta->name === 'tags' && '' !== $meta->value) { + $tags = explode(',', $meta->value); + } + } + + return $tags; + } + + /** + * @param Recurrence $recurrence + * @param int $page + * @param int $pageSize + * + * @return LengthAwarePaginator + */ + public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator + { + $journalMeta = TransactionJournalMeta + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get()->pluck('transaction_journal_id')->toArray(); + $search = []; + foreach ($journalMeta as $journalId) { + $search[] = ['id' => (int)$journalId]; + } + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser($recurrence->user); + $collector->withOpposingAccount()->setAllAssetAccounts()->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page); + // filter on specific journals. + $collector->removeFilter(InternalTransferFilter::class); + $collector->setJournals(new Collection($search)); + + return $collector->getPaginatedJournals(); + } + + /** + * @param Recurrence $recurrence + * + * @return Collection + */ + public function getTransactions(Recurrence $recurrence): Collection + { + $journalMeta = TransactionJournalMeta + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get()->pluck('transaction_journal_id')->toArray(); + $search = []; + foreach ($journalMeta as $journalId) { + $search[] = ['id' => (int)$journalId]; + } + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser($recurrence->user); + $collector->withOpposingAccount()->setAllAssetAccounts()->withCategoryInformation()->withBudgetInformation(); + // filter on specific journals. + $collector->removeFilter(InternalTransferFilter::class); + $collector->setJournals(new Collection($search)); + + return $collector->getJournals(); + } + + /** + * Calculate the next X iterations starting on the date given in $date. + * + * @param RecurrenceRepetition $repetition + * @param Carbon $date + * @param int $count + * + * @return array + * @throws FireflyException + */ + public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array + { + $return = []; + $mutator = clone $date; + $skipMod = $repetition->repetition_skip + 1; + $total = 0; + $attempts = 0; + switch ($repetition->repetition_type) { + default: + throw new FireflyException( + sprintf('Cannot calculate occurrences for recurring transaction repetition type "%s"', $repetition->repetition_type) + ); + case 'daily': + while ($total < $count) { + $mutator->addDay(); + if ($attempts % $skipMod === 0) { + $return[] = clone $mutator; + $total++; + } + $attempts++; + } + break; + case 'weekly': + // monday = 1 + // sunday = 7 + $mutator->addDay(); // always assume today has passed. + $dayOfWeek = (int)$repetition->repetition_moment; + if ($mutator->dayOfWeekIso > $dayOfWeek) { + // day has already passed this week, add one week: + $mutator->addWeek(); + } + // today is wednesday (3), expected is friday (5): add two days. + // today is friday (5), expected is monday (1), subtract four days. + $dayDifference = $dayOfWeek - $mutator->dayOfWeekIso; + $mutator->addDays($dayDifference); + + while ($total < $count) { + if ($attempts % $skipMod === 0) { + $return[] = clone $mutator; + $total++; + } + $attempts++; + $mutator->addWeek(); + } + break; + case 'monthly': + $mutator->addDay(); // always assume today has passed. + $dayOfMonth = (int)$repetition->repetition_moment; + if ($mutator->day > $dayOfMonth) { + // day has passed already, add a month. + $mutator->addMonth(); + } + + while ($total < $count) { + $domCorrected = min($dayOfMonth, $mutator->daysInMonth); + $mutator->day = $domCorrected; + if ($attempts % $skipMod === 0) { + $return[] = clone $mutator; + $total++; + } + $attempts++; + $mutator->endOfMonth()->addDay(); + } + break; + case 'ndom': + $mutator->addDay(); // always assume today has passed. + $mutator->startOfMonth(); + // this feels a bit like a cop out but why reinvent the wheel? + $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth',]; + $daysOfWeek = [1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday',]; + $parts = explode(',', $repetition->repetition_moment); + + while ($total < $count) { + $string = sprintf('%s %s of %s %s', $counters[$parts[0]], $daysOfWeek[$parts[1]], $mutator->format('F'), $mutator->format('Y')); + $newCarbon = new Carbon($string); + if ($attempts % $skipMod === 0) { + $return[] = clone $newCarbon; + $total++; + } + $attempts++; + $mutator->endOfMonth()->addDay(); + } + break; + case 'yearly': + $date = new Carbon($repetition->repetition_moment); + $date->year = $mutator->year; + if ($mutator > $date) { + $date->addYear(); + } + $obj = clone $date; + while ($total < $count) { + if ($attempts % $skipMod === 0) { + $return[] = clone $obj; + $total++; + } + $obj->addYears(1); + $attempts++; + } + break; + } + // filter out all the weekend days: + $return = $this->filterWeekends($repetition, $return); + + return $return; + } + + /** + * Parse the repetition in a string that is user readable. + * + * @param RecurrenceRepetition $repetition + * + * @return string + * @throws FireflyException + */ + public function repetitionDescription(RecurrenceRepetition $repetition): string + { + /** @var Preference $pref */ + $pref = app('preferences')->getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); + $language = $pref->data; + switch ($repetition->repetition_type) { + default: + throw new FireflyException(sprintf('Cannot translate recurring transaction repetition type "%s"', $repetition->repetition_type)); + break; + case 'daily': + return trans('firefly.recurring_daily', [], $language); + break; + case 'weekly': + $dayOfWeek = trans(sprintf('config.dow_%s', $repetition->repetition_moment), [], $language); + + return trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language); + break; + case 'monthly': + // format a date: + return trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment], $language); + break; + case 'ndom': + $parts = explode(',', $repetition->repetition_moment); + // first part is number of week, second is weekday. + $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $language); + + return trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language); + break; + case 'yearly': + // + $today = new Carbon; + $today->endOfYear(); + $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); + $diffInYears = $today->diffInYears($repDate); + $repDate->addYears($diffInYears); // technically not necessary. + $string = $repDate->formatLocalized(trans('config.month_and_day_no_year')); + + return trans('firefly.recurring_yearly', ['date' => $string], $language); + break; + + } + + } + + /** + * Set user for in repository. + * + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * @param array $data + * + * @return Recurrence + */ + public function store(array $data): Recurrence + { + $factory = new RecurrenceFactory; + $factory->setUser($this->user); + + return $factory->create($data); + } + + /** + * Update a recurring transaction. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + * @throws FireflyException + */ + public function update(Recurrence $recurrence, array $data): Recurrence + { + /** @var RecurrenceUpdateService $service */ + $service = app(RecurrenceUpdateService::class); + + return $service->update($recurrence, $data); + } + + /** + * Filters out all weekend entries, if necessary. + * + * @param RecurrenceRepetition $repetition + * @param array $dates + * + * @return array + */ + protected function filterWeekends(RecurrenceRepetition $repetition, array $dates): array + { + if ((int)$repetition->weekend === RecurrenceRepetition::WEEKEND_DO_NOTHING) { + Log::debug('Repetition will not be filtered on weekend days.'); + + return $dates; + } + $return = []; + /** @var Carbon $date */ + foreach ($dates as $date) { + $isWeekend = $date->isWeekend(); + if (!$isWeekend) { + $return[] = clone $date; + Log::debug(sprintf('Date is %s, not a weekend date.', $date->format('D d M Y'))); + continue; + } + + // is weekend and must set back to Friday? + if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_FRIDAY) { + $clone = clone $date; + $clone->addDays(5 - $date->dayOfWeekIso); + Log::debug( + sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) + ); + $return[] = clone $clone; + continue; + } + + // postpone to Monday? + if ($repetition->weekend === RecurrenceRepetition::WEEKEND_TO_MONDAY) { + $clone = clone $date; + $clone->addDays(8 - $date->dayOfWeekIso); + Log::debug( + sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) + ); + $return[] = $clone; + continue; + } + Log::debug(sprintf('Date is %s, removed from final result', $date->format('D d M Y'))); + } + + // filter unique dates + Log::debug(sprintf('Count before filtering: %d', \count($dates))); + $collection = new Collection($return); + $filtered = $collection->unique(); + $return = $filtered->toArray(); + + Log::debug(sprintf('Count after filtering: %d', \count($return))); + + return $return; + } +} \ No newline at end of file diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php new file mode 100644 index 0000000000..d738d768d6 --- /dev/null +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -0,0 +1,190 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Recurring; + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\User; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; + + +/** + * Interface RecurringRepositoryInterface + * + * @package FireflyIII\Repositories\Recurring + */ +interface RecurringRepositoryInterface +{ + /** + * Destroy a recurring transaction. + * + * @param Recurrence $recurrence + */ + public function destroy(Recurrence $recurrence): void; + + /** + * Returns all of the user's recurring transactions. + * + * @return Collection + */ + public function get(): Collection; + + /** + * Get ALL recurring transactions. + * + * @return Collection + */ + public function getAll(): Collection; + + /** + * Get the budget ID from a recurring transaction transaction. + * + * @param RecurrenceTransaction $recurrenceTransaction + * + * @return null|int + */ + public function getBudget(RecurrenceTransaction $recurrenceTransaction): ?int; + + /** + * Get the category from a recurring transaction transaction. + * + * @param RecurrenceTransaction $recurrenceTransaction + * + * @return null|string + */ + public function getCategory(RecurrenceTransaction $recurrenceTransaction): ?string; + + /** + * Returns the journals created for this recurrence, possibly limited by time. + * TODO make consistent with getTransactions + * + * @param Recurrence $recurrence + * @param Carbon|null $start + * @param Carbon|null $end + * + * @return Collection + */ + public function getJournals(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): Collection; + + /** + * Get the notes. + * + * @param Recurrence $recurrence + * + * @return string + */ + public function getNoteText(Recurrence $recurrence): string; + + /** + * Generate events in the date range. + * + * @param RecurrenceRepetition $repetition + * @param Carbon $start + * @param Carbon $end + * + * @throws FireflyException + * + * @return array + */ + public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array; + + /** + * Get the tags from the recurring transaction. + * + * @param Recurrence $recurrence + * + * @return array + */ + public function getTags(Recurrence $recurrence): array; + + /** + * @param Recurrence $recurrence + * @param int $page + * @param int $pageSize + * + * @return LengthAwarePaginator + */ + public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator; + + /** + * @param Recurrence $recurrence + * + * @return Collection + */ + public function getTransactions(Recurrence $recurrence): Collection; + + /** + * Calculate the next X iterations starting on the date given in $date. + * Returns an array of Carbon objects. + * + * @param RecurrenceRepetition $repetition + * @param Carbon $date + * @param int $count + * + * @throws FireflyException + * @return array + */ + public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array; + + /** + * Parse the repetition in a string that is user readable. + * + * @param RecurrenceRepetition $repetition + * + * @return string + */ + public function repetitionDescription(RecurrenceRepetition $repetition): string; + + /** + * Set user for in repository. + * + * @param User $user + */ + public function setUser(User $user): void; + + /** + * Store a new recurring transaction. + *\ + * + * @param array $data + * + * @return Recurrence + */ + public function store(array $data): Recurrence; + + /** + * Update a recurring transaction. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + */ + public function update(Recurrence $recurrence, array $data): Recurrence; + +} \ No newline at end of file diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 06d29aefa0..cb33208d77 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -80,6 +80,16 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } + /** + * Get all the users rules. + * + * @return Collection + */ + public function getAll(): Collection + { + return $this->user->rules()->with(['ruleGroup'])->get(); + } + /** * FIxXME can return null. * @@ -280,7 +290,7 @@ class RuleRepository implements RuleRepositoryInterface $rule->order = ($order + 1); $rule->active = 1; $rule->strict = $data['strict'] ?? false; - $rule->stop_processing = 1 === (int)$data['stop_processing']; + $rule->stop_processing = 1 === (int)$data['stop-processing']; $rule->title = $data['title']; $rule->description = \strlen($data['description']) > 0 ? $data['description'] : null; @@ -346,7 +356,7 @@ class RuleRepository implements RuleRepositoryInterface // update rule: $rule->rule_group_id = $data['rule_group_id']; $rule->active = $data['active']; - $rule->stop_processing = $data['stop_processing']; + $rule->stop_processing = $data['stop-processing']; $rule->title = $data['title']; $rule->strict = $data['strict'] ?? false; $rule->description = $data['description']; @@ -377,11 +387,11 @@ class RuleRepository implements RuleRepositoryInterface { $order = 1; foreach ($data['rule-actions'] as $index => $action) { - $value = $data['rule-action-values'][$index] ?? ''; - $stopProcessing = isset($data['rule-action-stop'][$index]) ? true : false; + $value = $action['value'] ?? ''; + $stopProcessing = $action['stop-processing'] ?? false; $actionValues = [ - 'action' => $action, + 'action' => $action['name'], 'value' => $value, 'stopProcessing' => $stopProcessing, 'order' => $order, @@ -413,11 +423,11 @@ class RuleRepository implements RuleRepositoryInterface $this->storeTrigger($rule, $triggerValues); foreach ($data['rule-triggers'] as $index => $trigger) { - $value = $data['rule-trigger-values'][$index] ?? ''; - $stopProcessing = isset($data['rule-trigger-stop'][$index]) ? true : false; + $value = $trigger['value'] ?? ''; + $stopProcessing = $trigger['stop-processing'] ?? false; $triggerValues = [ - 'action' => $trigger, + 'action' => $trigger['name'], 'value' => $value, 'stopProcessing' => $stopProcessing, 'order' => $order, diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 5fcdd4c89d..cefbf46a95 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -54,6 +54,13 @@ interface RuleRepositoryInterface */ public function find(int $ruleId): Rule; + /** + * Get all the users rules. + * + * @return Collection + */ + public function getAll(): Collection; + /** * @return RuleGroup */ diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 90c545cb58..2cefe02b68 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -136,6 +136,16 @@ class TagRepository implements TagRepositoryInterface return new Tag; } + /** + * @param int $tagId + * + * @return Tag|null + */ + public function findNull(int $tagId): ?Tag + { + return $this->user->tags()->find($tagId); + } + /** * @param Tag $tag * @@ -192,6 +202,16 @@ class TagRepository implements TagRepositoryInterface return new Carbon; } + /** + * Will return the newest tag (if known) or NULL. + * + * @return Tag|null + */ + public function newestTag(): ?Tag + { + return $this->user->tags()->whereNotNull('date')->orderBy('date', 'DESC')->first(); + } + /** * @return Tag */ @@ -453,23 +473,4 @@ class TagRepository implements TagRepositoryInterface return (int)($range[0] + $extra); } - - /** - * @param int $tagId - * - * @return Tag|null - */ - public function findNull(int $tagId): ?Tag - { - return $this->user->tags()->find($tagId); - } - - /** - * Will return the newest tag (if known) or NULL. - * - * @return Tag|null - */ - public function newestTag(): ?Tag - { - return $this->user->tags()->whereNotNull('date')->orderBy('date', 'DESC')->first(); -}} +} diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index e4e8b44ba5..3b6b7cd19a 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -69,12 +69,6 @@ interface TagRepositoryInterface /** * @param int $tagId * - * @return Tag|null - */ - public function findNull(int $tagId): ?Tag; - - /** - * @param int $tagId * @deprecated * @return Tag */ @@ -87,6 +81,13 @@ interface TagRepositoryInterface */ public function findByTag(string $tag): Tag; + /** + * @param int $tagId + * + * @return Tag|null + */ + public function findNull(int $tagId): ?Tag; + /** * @param Tag $tag * @@ -117,15 +118,18 @@ interface TagRepositoryInterface /** * Will return the newest tag (if known) or NULL. + * + * @return Tag|null + */ + public function newestTag(): ?Tag; + + /** + * Will return the newest tag (if known) or NULL. + * * @return Tag|null */ public function oldestTag(): ?Tag; - /** - * Will return the newest tag (if known) or NULL. - * @return Tag|null - */ - public function newestTag(): ?Tag; /** * @param User $user */ diff --git a/app/Rules/IsAssetAccountId.php b/app/Rules/IsAssetAccountId.php new file mode 100644 index 0000000000..7ffa717037 --- /dev/null +++ b/app/Rules/IsAssetAccountId.php @@ -0,0 +1,46 @@ +find($accountId); + if (null === $account) { + return false; + } + if ($account->accountType->type !== AccountType::ASSET && $account->accountType->type !== AccountType::DEFAULT) { + return false; + } + + return true; + } +} diff --git a/app/Rules/IsValidAttachmentModel.php b/app/Rules/IsValidAttachmentModel.php new file mode 100644 index 0000000000..954e5a2151 --- /dev/null +++ b/app/Rules/IsValidAttachmentModel.php @@ -0,0 +1,88 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Rules; + + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use Illuminate\Contracts\Validation\Rule; + +/** + * Class IsValidAttachmentModel + */ +class IsValidAttachmentModel implements Rule +{ + /** @var string */ + private $model; + + /** + * IsValidAttachmentModel constructor. + * + * @param string $model + */ + public function __construct(string $model) + { + $this->model = $model; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message(): string + { + return trans('validation.model_id_invalid'); + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + * @throws FireflyException + */ + public function passes($attribute, $value): bool + { + if (!auth()->check()) { + return false; + } + $user = auth()->user(); + switch ($this->model) { + default: + throw new FireflyException(sprintf('Model "%s" cannot be validated.', $this->model)); + case TransactionJournal::class: + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $repository->setUser($user); + $result = $repository->findNull((int)$value); + + return null !== $result; + break; + } + } +} \ No newline at end of file diff --git a/app/Rules/ValidRecurrenceRepetitionType.php b/app/Rules/ValidRecurrenceRepetitionType.php new file mode 100644 index 0000000000..c8aa3eedbd --- /dev/null +++ b/app/Rules/ValidRecurrenceRepetitionType.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Rules; + +use Illuminate\Contracts\Validation\Rule; + +/** + * Class ValidRecurrenceRepetitionType + */ +class ValidRecurrenceRepetitionType implements Rule +{ + + /** + * Get the validation error message. + * + * @return string + */ + public function message(): string + { + return trans('validation.valid_recurrence_rep_type'); + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + public function passes($attribute, $value): bool + { + $value = (string)$value; + if ($value === 'daily') { + return true; + } + //monthly,17 + //ndom,3,7 + if (\in_array(substr($value, 0, 6), ['yearly', 'weekly'])) { + return true; + } + if (0 === strpos($value, 'monthly')) { + return true; + } + if (0 === strpos($value, 'ndom')) { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/app/Rules/ValidRecurrenceRepetitionValue.php b/app/Rules/ValidRecurrenceRepetitionValue.php new file mode 100644 index 0000000000..3e2afe32a5 --- /dev/null +++ b/app/Rules/ValidRecurrenceRepetitionValue.php @@ -0,0 +1,107 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Rules; + + +use Carbon\Carbon; +use Illuminate\Contracts\Validation\Rule; +use InvalidArgumentException; + +/** + * Class ValidRecurrenceRepetitionValue + */ +class ValidRecurrenceRepetitionValue implements Rule +{ + + /** + * Get the validation error message. + * + * @return string + */ + public function message(): string + { + return trans('validation.valid_recurrence_rep_type'); + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + public function passes($attribute, $value): bool + { + $value = (string)$value; + + if ($value === 'daily') { + return true; + } + + if (0 === strpos($value, 'monthly')) { + $dayOfMonth = (int)substr($value, 8); + + return $dayOfMonth > 0 && $dayOfMonth < 32; + } + + //ndom,3,7 + // nth x-day of the month. + if (0 === strpos($value, 'ndom')) { + $parameters = explode(',', substr($value, 5)); + if (\count($parameters) !== 2) { + return false; + } + $nthDay = (int)($parameters[0] ?? 0.0); + $dayOfWeek = (int)($parameters[1] ?? 0.0); + if ($nthDay < 1 || $nthDay > 5) { + return false; + } + + return $dayOfWeek > 0 && $dayOfWeek < 8; + } + + //weekly,7 + if (0 === strpos($value, 'weekly')) { + $dayOfWeek = (int)substr($value, 7); + + return $dayOfWeek > 0 && $dayOfWeek < 8; + } + + //yearly,2018-01-01 + if (0 === strpos($value, 'yearly')) { + // rest of the string must be valid date: + $dateString = substr($value, 7); + try { + $date = Carbon::createFromFormat('Y-m-d', $dateString); + } catch (InvalidArgumentException $e) { + return false; + } + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/app/Services/Currency/FixerIO.php b/app/Services/Currency/FixerIO.php deleted file mode 100644 index 561cac7315..0000000000 --- a/app/Services/Currency/FixerIO.php +++ /dev/null @@ -1,96 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Services\Currency; - -use Carbon\Carbon; -use Exception; -use FireflyIII\Models\CurrencyExchangeRate; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\User; -use Log; -use Requests; - -/** - * Class FixerIO. - */ -class FixerIO implements ExchangeRateInterface -{ - /** @var User */ - protected $user; - - /** - * @param TransactionCurrency $fromCurrency - * @param TransactionCurrency $toCurrency - * @param Carbon $date - * - * @return CurrencyExchangeRate - */ - public function getRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate - { - $uri = sprintf('https://api.fixer.io/%s?base=%s&symbols=%s', $date->format('Y-m-d'), $fromCurrency->code, $toCurrency->code); - $statusCode = -1; - try { - $result = Requests::get($uri); - $statusCode = $result->status_code; - $body = $result->body; - } catch (Exception $e) { - // don't care about error - $body = sprintf('Requests_Exception: %s', $e->getMessage()); - } - // Requests_Exception - $rate = 1.0; - $content = null; - if (200 !== $statusCode) { - Log::error(sprintf('Something went wrong. Received error code %d and body "%s" from FixerIO.', $statusCode, $body)); - } - // get rate from body: - if (200 === $statusCode) { - $content = json_decode($body, true); - } - if (null !== $content) { - $code = $toCurrency->code; - $rate = $content['rates'][$code] ?? '1'; - } - - // create new currency exchange rate object: - $exchangeRate = new CurrencyExchangeRate; - $exchangeRate->user()->associate($this->user); - $exchangeRate->fromCurrency()->associate($fromCurrency); - $exchangeRate->toCurrency()->associate($toCurrency); - $exchangeRate->date = $date; - $exchangeRate->rate = $rate; - $exchangeRate->save(); - - return $exchangeRate; - } - - /** - * @param User $user - * - * @return mixed|void - */ - public function setUser(User $user) - { - $this->user = $user; - } -} diff --git a/app/Services/Currency/FixerIOv2.php b/app/Services/Currency/FixerIOv2.php index 67d4a77c82..b224e65de4 100644 --- a/app/Services/Currency/FixerIOv2.php +++ b/app/Services/Currency/FixerIOv2.php @@ -27,8 +27,9 @@ use Exception; use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Log; -use Requests; /** * Class FixerIOv2. @@ -48,19 +49,21 @@ class FixerIOv2 implements ExchangeRateInterface public function getRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate { // create new exchange rate with default values. - // create new currency exchange rate object: + $rate = 0; $exchangeRate = new CurrencyExchangeRate; $exchangeRate->user()->associate($this->user); $exchangeRate->fromCurrency()->associate($fromCurrency); $exchangeRate->toCurrency()->associate($toCurrency); $exchangeRate->date = $date; - $exchangeRate->rate = 0; + $exchangeRate->rate = $rate; // get API key $apiKey = env('FIXER_API_KEY', ''); // if no API key, return unsaved exchange rate. - if (\strlen($apiKey) === 0) { + if ('' === $apiKey) { + Log::warning('No fixer.IO API key, will not do conversion.'); + return $exchangeRate; } @@ -72,31 +75,36 @@ class FixerIOv2 implements ExchangeRateInterface $statusCode = -1; Log::debug(sprintf('Going to request exchange rate using URI %s', str_replace($apiKey, 'xxxx', $uri))); try { - $result = Requests::get($uri); - $statusCode = $result->status_code; - $body = $result->body; + $client = new Client; + $res = $client->request('GET', $uri); + $statusCode = $res->getStatusCode(); + $body = $res->getBody()->getContents(); Log::debug(sprintf('Result status code is %d', $statusCode)); - } catch (Exception $e) { + Log::debug(sprintf('Result body is: %s', $body)); + } catch (GuzzleException|Exception $e) { // don't care about error - $body = sprintf('Requests_Exception: %s', $e->getMessage()); + $body = sprintf('Guzzle exception: %s', $e->getMessage()); } - // Requests_Exception $content = null; if (200 !== $statusCode) { Log::error(sprintf('Something went wrong. Received error code %d and body "%s" from FixerIO.', $statusCode, $body)); } + $success = false; // get rate from body: if (200 === $statusCode) { $content = json_decode($body, true); + $success = $content['success'] ?? false; } - if (null !== $content) { + if (null !== $content && true === $success) { $code = $toCurrency->code; $rate = (float)($content['rates'][$code] ?? 0); + Log::debug('Got the following rates from Fixer: ', $content['rates'] ?? []); } - Log::debug('Got the following rates from Fixer: ', $content['rates'] ?? []); + $exchangeRate->rate = $rate; if ($rate !== 0.0) { + Log::debug('Rate is not zero, save it!'); $exchangeRate->save(); } diff --git a/app/Services/Github/Request/GithubRequest.php b/app/Services/Github/Request/GithubRequest.php index 3e7a5ca5d6..fc3552a18c 100644 --- a/app/Services/Github/Request/GithubRequest.php +++ b/app/Services/Github/Request/GithubRequest.php @@ -29,6 +29,6 @@ namespace FireflyIII\Services\Github\Request; */ interface GithubRequest { - public function call(); + public function call(): void; } diff --git a/app/Services/Github/Request/UpdateRequest.php b/app/Services/Github/Request/UpdateRequest.php index 9fb0f212dd..fd2d173049 100644 --- a/app/Services/Github/Request/UpdateRequest.php +++ b/app/Services/Github/Request/UpdateRequest.php @@ -26,7 +26,9 @@ namespace FireflyIII\Services\Github\Request; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Services\Github\Object\Release; -use Requests; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use Log; use SimpleXMLElement; /** @@ -41,30 +43,34 @@ class UpdateRequest implements GithubRequest * * @throws FireflyException */ - public function call() + public function call(): void { $uri = 'https://github.com/firefly-iii/firefly-iii/releases.atom'; + Log::debug(sprintf('Going to call %s', $uri)); try { - $response = Requests::get($uri); - } catch (Exception $e) { + $client = new Client(); + $res = $client->request('GET', $uri); + } catch (GuzzleException|Exception $e) { throw new FireflyException(sprintf('Response error from Github: %s', $e->getMessage())); } - if ($response->status_code !== 200) { - throw new FireflyException(sprintf('Returned code %d, error: %s', $response->status_code, $response->body)); + if ($res->getStatusCode() !== 200) { + throw new FireflyException(sprintf('Returned code %d, error: %s', $res->getStatusCode(), $res->getBody()->getContents())); } - $releaseXml = new SimpleXMLElement($response->body, LIBXML_NOCDATA); + $releaseXml = new SimpleXMLElement($res->getBody()->getContents(), LIBXML_NOCDATA); //fetch the products for each category if (isset($releaseXml->entry)) { + Log::debug(sprintf('Count of entries is: %d', \count($releaseXml->entry))); foreach ($releaseXml->entry as $entry) { - $array = [ + $array = [ 'id' => (string)$entry->id, 'updated' => (string)$entry->updated, 'title' => (string)$entry->title, 'content' => (string)$entry->content, ]; + Log::debug(sprintf('Found version %s', $entry->title)); $this->releases[] = new Release($array); } } diff --git a/app/Services/IP/IpifyOrg.php b/app/Services/IP/IpifyOrg.php index 2e72e23c9c..0c0f8a3404 100644 --- a/app/Services/IP/IpifyOrg.php +++ b/app/Services/IP/IpifyOrg.php @@ -24,8 +24,9 @@ declare(strict_types=1); namespace FireflyIII\Services\IP; use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Log; -use Requests; /** * Class IpifyOrg @@ -42,19 +43,20 @@ class IpifyOrg implements IPRetrievalInterface { $result = null; try { - $response = Requests::get('https://api.ipify.org'); - } catch (Exception $e) { + $client = new Client; + $res = $client->request('GET', 'https://api.ipify.org'); + } catch (GuzzleException|Exception $e) { Log::warning(sprintf('The ipify.org service could not retrieve external IP: %s', $e->getMessage())); Log::warning($e->getTraceAsString()); return null; } - if (200 !== $response->status_code) { - Log::warning(sprintf('Could not retrieve external IP: %d %s', $response->status_code, $response->body)); + if (200 !== $res->getStatusCode()) { + Log::warning(sprintf('Could not retrieve external IP: %d %s', $res->getStatusCode(), $res->getBody()->getContents())); return null; } - return (string)$response->body; + return (string)$res->getBody()->getContents(); } } diff --git a/app/Services/Internal/Destroy/RecurrenceDestroyService.php b/app/Services/Internal/Destroy/RecurrenceDestroyService.php new file mode 100644 index 0000000000..6f59892f4f --- /dev/null +++ b/app/Services/Internal/Destroy/RecurrenceDestroyService.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Destroy; + +use Exception; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceTransaction; +use Log; + +/** + * @codeCoverageIgnore + * Class RecurrenceDestroyService + */ +class RecurrenceDestroyService +{ + /** + * @param Recurrence $recurrence + */ + public function destroy(Recurrence $recurrence): void + { + try { + // delete all meta data + $recurrence->recurrenceMeta()->delete(); + + // delete all transactions. + /** @var RecurrenceTransaction $transaction */ + foreach($recurrence->recurrenceTransactions as $transaction) { + $transaction->recurrenceTransactionMeta()->delete(); + $transaction->delete(); + } + // delete all repetitions + $recurrence->recurrenceRepetitions()->delete(); + + // delete recurrence + $recurrence->delete(); + } catch (Exception $e) { // @codeCoverageIgnore + Log::error(sprintf('Could not delete recurrence: %s', $e->getMessage())); // @codeCoverageIgnore + } + } + +} diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php new file mode 100644 index 0000000000..b36d7d4c0d --- /dev/null +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -0,0 +1,214 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Support; + +use Exception; +use FireflyIII\Factory\BudgetFactory; +use FireflyIII\Factory\CategoryFactory; +use FireflyIII\Factory\PiggyBankFactory; +use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Models\TransactionType; +use Log; + + +/** + * Trait RecurringTransactionTrait + * + * @package FireflyIII\Services\Internal\Support + */ +trait RecurringTransactionTrait +{ + /** + * @param Recurrence $recurrence + * @param array $repetitions + */ + public function createRepetitions(Recurrence $recurrence, array $repetitions): void + { + /** @var array $array */ + foreach ($repetitions as $array) { + RecurrenceRepetition::create( + [ + 'recurrence_id' => $recurrence->id, + 'repetition_type' => $array['type'], + 'repetition_moment' => $array['moment'], + 'repetition_skip' => $array['skip'], + 'weekend' => $array['weekend'] ?? 1, + ] + ); + + } + } + + /** + * @param Recurrence $recurrence + * @param array $transactions + */ + public function createTransactions(Recurrence $recurrence, array $transactions): void + { + foreach ($transactions as $array) { + $source = null; + $destination = null; + switch ($recurrence->transactionType->type) { + case TransactionType::WITHDRAWAL: + $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::EXPENSE, $array['destination_id'], $array['destination_name']); + break; + case TransactionType::DEPOSIT: + $source = $this->findAccount(AccountType::REVENUE, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']); + break; + case TransactionType::TRANSFER: + $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']); + break; + } + + /** @var TransactionCurrencyFactory $factory */ + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($array['currency_id'], $array['currency_code']); + $foreignCurrency = $factory->find($array['foreign_currency_id'], $array['foreign_currency_code']); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser($recurrence->user); + if (null === $currency) { + $currency = $defaultCurrency; + } + $transaction = new RecurrenceTransaction( + [ + 'recurrence_id' => $recurrence->id, + 'transaction_currency_id' => $currency->id, + 'foreign_currency_id' => null === $foreignCurrency ? null : $foreignCurrency->id, + 'source_id' => $source->id, + 'destination_id' => $destination->id, + 'amount' => $array['amount'], + 'foreign_amount' => '' === (string)$array['foreign_amount'] ? null : (string)$array['foreign_amount'], + 'description' => $array['description'], + ] + ); + $transaction->save(); + + /** @var BudgetFactory $budgetFactory */ + $budgetFactory = app(BudgetFactory::class); + $budgetFactory->setUser($recurrence->user); + $budget = $budgetFactory->find($array['budget_id'], $array['budget_name']); + + /** @var CategoryFactory $categoryFactory */ + $categoryFactory = app(CategoryFactory::class); + $categoryFactory->setUser($recurrence->user); + $category = $categoryFactory->findOrCreate($array['category_id'], $array['category_name']); + + // create recurrence transaction meta: + if (null !== $budget) { + RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'budget_id', + 'value' => $budget->id, + ] + ); + } + if (null !== $category) { + RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'category_name', + 'value' => $category->name, + ] + ); + } + } + } + + /** + * @param Recurrence $recurrence + */ + public function deleteRepetitions(Recurrence $recurrence): void + { + $recurrence->recurrenceRepetitions()->delete(); + } + + /** + * @param Recurrence $recurrence + */ + public function deleteTransactions(Recurrence $recurrence): void + { + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transaction->recurrenceTransactionMeta()->delete(); + try { + $transaction->delete(); + } catch (Exception $e) { + Log::debug($e->getMessage()); + } + } + } + + /** + * @param Recurrence $recurrence + * @param array $data + */ + public function updateMetaData(Recurrence $recurrence, array $data): void + { + // only two special meta fields right now. Let's just hard code them. + $piggyId = (int)($data['meta']['piggy_bank_id'] ?? 0.0); + $piggyName = $data['meta']['piggy_bank_name'] ?? ''; + /** @var PiggyBankFactory $factory */ + $factory = app(PiggyBankFactory::class); + $factory->setUser($recurrence->user); + $piggyBank = $factory->find($piggyId, $piggyName); + if (null !== $piggyBank) { + /** @var RecurrenceMeta $entry */ + $entry = $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->first(); + if (null === $entry) { + $entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'piggy_bank_id', 'value' => $piggyBank->id]); + } + $entry->value = $piggyBank->id; + $entry->save(); + } + if (null === $piggyBank) { + // delete if present + $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->delete(); + } + + + $tags = $data['meta']['tags'] ?? []; + if (\count($tags) > 0) { + /** @var RecurrenceMeta $entry */ + $entry = $recurrence->recurrenceMeta()->where('name', 'tags')->first(); + if (null === $entry) { + $entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'tags', 'value' => implode(',', $tags)]); + } + $entry->value = implode(',', $tags); + $entry->save(); + } + if (\count($tags) === 0) { + // delete if present + $recurrence->recurrenceMeta()->where('name', 'tags')->delete(); + } + } +} \ No newline at end of file diff --git a/app/Services/Internal/Support/TransactionServiceTrait.php b/app/Services/Internal/Support/TransactionServiceTrait.php index 06fe1fe99c..d0cb0a3f8f 100644 --- a/app/Services/Internal/Support/TransactionServiceTrait.php +++ b/app/Services/Internal/Support/TransactionServiceTrait.php @@ -190,6 +190,7 @@ trait TransactionServiceTrait */ protected function findCategory(?int $categoryId, ?string $categoryName): ?Category { + Log::debug(sprintf('Going to find or create category #%d, with name "%s"', $categoryId, $categoryName)); /** @var CategoryFactory $factory */ $factory = app(CategoryFactory::class); $factory->setUser($this->user); diff --git a/app/Services/Internal/Support/TransactionTypeTrait.php b/app/Services/Internal/Support/TransactionTypeTrait.php new file mode 100644 index 0000000000..204110c66b --- /dev/null +++ b/app/Services/Internal/Support/TransactionTypeTrait.php @@ -0,0 +1,57 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Support; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\TransactionTypeFactory; +use FireflyIII\Models\TransactionType; +use Log; + +/** + * Trait TransactionTypeTrait + * + * @package FireflyIII\Services\Internal\Support + */ +trait TransactionTypeTrait +{ + /** + * Get the transaction type. Since this is mandatory, will throw an exception when nothing comes up. Will always + * use TransactionType repository. + * + * @param string $type + * + * @return TransactionType + * @throws FireflyException + */ + protected function findTransactionType(string $type): TransactionType + { + $factory = app(TransactionTypeFactory::class); + $transactionType = $factory->find($type); + if (null === $transactionType) { + Log::error(sprintf('Could not find transaction type for "%s"', $type)); // @codeCoverageIgnore + throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); // @codeCoverageIgnore + } + + return $transactionType; + } +} \ No newline at end of file diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index 97e89b1753..72c811bee2 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -51,7 +51,7 @@ class BillUpdateService $bill->repeat_freq = $data['repeat_freq']; $bill->skip = $data['skip']; $bill->automatch = true; - $bill->active = $data['active']; + $bill->active = $data['active']??true; $bill->save(); // update note: diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php new file mode 100644 index 0000000000..241a36cf50 --- /dev/null +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -0,0 +1,91 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Update; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; +use FireflyIII\Services\Internal\Support\TransactionServiceTrait; +use FireflyIII\Services\Internal\Support\TransactionTypeTrait; +use FireflyIII\User; + +/** + * Class RecurrenceUpdateService + */ +class RecurrenceUpdateService +{ + use TransactionTypeTrait, TransactionServiceTrait, RecurringTransactionTrait; + + /** @var User */ + private $user; + + /** + * Updates a recurrence. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + * @throws FireflyException + */ + public function update(Recurrence $recurrence, array $data): Recurrence + { + // is expected by TransactionServiceTrait + $this->user = $recurrence->user; + $transactionType = $this->findTransactionType(ucfirst($data['recurrence']['type'])); + // update basic fields first: + $recurrence->transaction_type_id = $transactionType->id; + $recurrence->title = $data['recurrence']['title'] ?? $recurrence->title; + $recurrence->description = $data['recurrence']['description'] ?? $recurrence->description; + $recurrence->first_date = $data['recurrence']['first_date'] ?? $recurrence->first_date; + $recurrence->repeat_until = $data['recurrence']['repeat_until'] ?? $recurrence->repeat_until; + $recurrence->repetitions = $data['recurrence']['repetitions'] ?? $recurrence->repetitions; + $recurrence->apply_rules = $data['recurrence']['apply_rules'] ?? $recurrence->apply_rules; + $recurrence->active = $data['recurrence']['active'] ?? $recurrence->active; + + if(isset($data['recurrence']['repetition_end'])) { + if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'until_date'])) { + $recurrence->repetitions = 0; + } + if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'times'])) { + $recurrence->repeat_until = null; + } + } + $recurrence->save(); + + // update all meta data: + $this->updateMetaData($recurrence, $data); + + // update all repetitions + $this->deleteRepetitions($recurrence); + $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + + // update all transactions (and associated meta-data); + $this->deleteTransactions($recurrence); + $this->createTransactions($recurrence, $data['transactions'] ?? []); + + return $recurrence; + } +} \ No newline at end of file diff --git a/app/Services/Password/PwndVerifier.php b/app/Services/Password/PwndVerifier.php deleted file mode 100644 index d5bc349253..0000000000 --- a/app/Services/Password/PwndVerifier.php +++ /dev/null @@ -1,56 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Services\Password; - -use Exception; -use Log; -use Requests; - -/** - * Class PwndVerifier. - */ -class PwndVerifier implements Verifier -{ - /** - * Verify the given password against (some) service. - * - * @param string $password - * - * @return bool - */ - public function validPassword(string $password): bool - { - $hash = sha1($password); - $uri = sprintf('https://haveibeenpwned.com/api/v2/pwnedpassword/%s', $hash); - $opt = ['useragent' => 'Firefly III v' . config('firefly.version'), 'timeout' => 2]; - - try { - $result = Requests::get($uri, ['originalPasswordIsAHash' => 'true'], $opt); - } catch (Exception $e) { - return true; - } - Log::debug(sprintf('Status code returned is %d', $result->status_code)); - - return 404 === $result->status_code; - } -} diff --git a/app/Services/Password/PwndVerifierV2.php b/app/Services/Password/PwndVerifierV2.php index bf3cd330cf..014fbbcbe6 100644 --- a/app/Services/Password/PwndVerifierV2.php +++ b/app/Services/Password/PwndVerifierV2.php @@ -23,8 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Services\Password; use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Log; -use Requests; /** * Class PwndVerifierV2. @@ -44,27 +45,30 @@ class PwndVerifierV2 implements Verifier $prefix = substr($hash, 0, 5); $rest = substr($hash, 5); $uri = sprintf('https://api.pwnedpasswords.com/range/%s', $prefix); - $opt = ['useragent' => 'Firefly III v' . config('firefly.version'), 'timeout' => 2]; + $opt = [ + 'headers' => ['User-Agent' => 'Firefly III v' . config('firefly.version')], + 'timeout' => 2]; Log::debug(sprintf('hash prefix is %s', $prefix)); Log::debug(sprintf('rest is %s', $rest)); try { - $result = Requests::get($uri, $opt); - } catch (Exception $e) { + $client = new Client(); + $res = $client->request('GET', $uri, $opt); + } catch (GuzzleException|Exception $e) { return true; } - Log::debug(sprintf('Status code returned is %d', $result->status_code)); - if (404 === $result->status_code) { + Log::debug(sprintf('Status code returned is %d', $res->getStatusCode())); + if (404 === $res->getStatusCode()) { return true; } - $strpos = stripos($result->body, $rest); + $strpos = stripos($res->getBody()->getContents(), $rest); if ($strpos === false) { Log::debug(sprintf('%s was not found in result body. Return true.', $rest)); return true; } - Log::debug('Could not find %s, return FALSE.'); + Log::debug(sprintf('Could not find %s, return FALSE.', $rest)); return false; } diff --git a/app/Services/Spectre/Object/Attempt.php b/app/Services/Spectre/Object/Attempt.php index 6aecd25246..495fa9566e 100644 --- a/app/Services/Spectre/Object/Attempt.php +++ b/app/Services/Spectre/Object/Attempt.php @@ -87,9 +87,9 @@ class Attempt extends SpectreObject private $successAt; /** @var Carbon */ private $toDate; - /** @var Carbon */ + /** @var Carbon */ private $updatedAt; // undocumented -/** @var string */ + /** @var string */ private $userAgent; /** diff --git a/app/Services/Spectre/Request/NewCustomerRequest.php b/app/Services/Spectre/Request/NewCustomerRequest.php index eea0b17c05..099a40cfd9 100644 --- a/app/Services/Spectre/Request/NewCustomerRequest.php +++ b/app/Services/Spectre/Request/NewCustomerRequest.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Services\Spectre\Request; use FireflyIII\Services\Spectre\Object\Customer; - +use Log; /** * Class NewCustomerRequest */ @@ -43,6 +43,7 @@ class NewCustomerRequest extends SpectreRequest ], ]; $uri = '/api/v4/customers/'; + Log::debug(sprintf('Going to call %s with info:', $uri), $data); $response = $this->sendSignedSpectrePost($uri, $data); // create customer: $this->customer = new Customer($response['data']); diff --git a/app/Services/Spectre/Request/SpectreRequest.php b/app/Services/Spectre/Request/SpectreRequest.php index 6b735de91b..1fafadf23c 100644 --- a/app/Services/Spectre/Request/SpectreRequest.php +++ b/app/Services/Spectre/Request/SpectreRequest.php @@ -25,9 +25,9 @@ namespace FireflyIII\Services\Spectre\Request; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\User; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Log; -use Requests; -use Requests_Response; /** * Class SpectreRequest @@ -195,23 +195,24 @@ abstract class SpectreRequest } $headers = $this->getDefaultHeaders(); - $body = json_encode($data); + $sendBody = json_encode($data); // OK $fullUri = $this->server . $uri; - $signature = $this->generateSignature('get', $fullUri, $body); + $signature = $this->generateSignature('get', $fullUri, $sendBody); $headers['Signature'] = $signature; Log::debug('Final headers for spectre signed get request:', $headers); try { - $response = Requests::get($fullUri, $headers); - } catch (Exception $e) { - throw new FireflyException(sprintf('Request Exception: %s', $e->getMessage())); + $client = new Client; + $res = $client->request('GET', $fullUri, ['headers' => $headers]); + } catch (GuzzleException|Exception $e) { + throw new FireflyException(sprintf('Guzzle Exception: %s', $e->getMessage())); } - $this->detectError($response); - $statusCode = (int)$response->status_code; + $statusCode = $res->getStatusCode(); + $returnBody = $res->getBody()->getContents(); + $this->detectError($returnBody, $statusCode); - $body = $response->body; - $array = json_decode($body, true); - $responseHeaders = $response->headers->getAll(); + $array = json_decode($returnBody, true); + $responseHeaders = $res->getHeaders(); $array['ResponseHeaders'] = $responseHeaders; $array['ResponseStatusCode'] = $statusCode; @@ -220,6 +221,7 @@ abstract class SpectreRequest throw new FireflyException(sprintf('Error of class %s: %s', $array['error_class'], $message)); } + return $array; } @@ -245,28 +247,32 @@ abstract class SpectreRequest Log::debug('Final headers for spectre signed POST request:', $headers); try { - $response = Requests::post($fullUri, $headers, $body); - } catch (Exception $e) { + $client = new Client; + $res = $client->request('POST', $fullUri, ['headers' => $headers, 'body' => $body]); + } catch (GuzzleException|Exception $e) { throw new FireflyException(sprintf('Request Exception: %s', $e->getMessage())); } - $this->detectError($response); - $body = $response->body; + $body = $res->getBody()->getContents(); + $statusCode = $res->getStatusCode(); + $this->detectError($body, $statusCode); + $array = json_decode($body, true); - $responseHeaders = $response->headers->getAll(); + $responseHeaders = $res->getHeaders(); $array['ResponseHeaders'] = $responseHeaders; - $array['ResponseStatusCode'] = $response->status_code; + $array['ResponseStatusCode'] = $statusCode; return $array; } /** - * @param Requests_Response $response + * @param string $body + * + * @param int $statusCode * * @throws FireflyException */ - private function detectError(Requests_Response $response): void + private function detectError(string $body, int $statusCode): void { - $body = $response->body; $array = json_decode($body, true); if (isset($array['error_class'])) { $message = $array['error_message'] ?? '(no message)'; @@ -279,9 +285,8 @@ abstract class SpectreRequest throw new FireflyException(sprintf('Error of class %s: %s', $errorClass, $message)); } - $statusCode = (int)$response->status_code; if (200 !== $statusCode) { - throw new FireflyException(sprintf('Status code %d: %s', $statusCode, $response->body)); + throw new FireflyException(sprintf('Status code %d: %s', $statusCode, $body)); } } } diff --git a/app/Support/Binder/SimpleJournalList.php b/app/Support/Binder/SimpleJournalList.php index c356331b48..070aa6dd06 100644 --- a/app/Support/Binder/SimpleJournalList.php +++ b/app/Support/Binder/SimpleJournalList.php @@ -28,7 +28,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Routing\Route; use Illuminate\Support\Collection; -use Session; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -58,7 +57,7 @@ class SimpleJournalList implements BinderInterface // prep some vars $messages = []; - $final = new Collection; + $final = new Collection; /** @var JournalRepositoryInterface $repository */ $repository = app(JournalRepositoryInterface::class); diff --git a/app/Support/CacheProperties.php b/app/Support/CacheProperties.php index 0e0be45819..d6d34aa8ed 100644 --- a/app/Support/CacheProperties.php +++ b/app/Support/CacheProperties.php @@ -24,7 +24,6 @@ namespace FireflyIII\Support; use Cache; use Illuminate\Support\Collection; -use Preferences as Prefs; /** * Class CacheProperties. @@ -44,7 +43,7 @@ class CacheProperties $this->properties = new Collection; if (auth()->check()) { $this->addProperty(auth()->user()->id); - $this->addProperty(Prefs::lastActivity()); + $this->addProperty(app('preferences')->lastActivity()); } } diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 17467594c0..24669d64a0 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -27,14 +27,17 @@ use Carbon\Carbon; use Eloquent; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Form; use Illuminate\Support\Collection; use Illuminate\Support\MessageBag; +use Log; use RuntimeException; use Session; @@ -43,6 +46,45 @@ use Session; */ class ExpandedForm { + /** + * @param string $name + * @param null $options + * + * @return string + * @throws \Throwable + */ + public function activeAssetAccountList(string $name, $value = null, array $options = []): string + { + // make repositories + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + /** @var CurrencyRepositoryInterface $currencyRepos */ + $currencyRepos = app(CurrencyRepositoryInterface::class); + + $assetAccounts = $repository->getActiveAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $grouped = []; + // group accounts: + /** @var Account $account */ + foreach ($assetAccounts as $account) { + $balance = app('steam')->balance($account, new Carbon); + $currencyId = (int)$account->getMeta('currency_id'); + $currency = $currencyRepos->findNull($currencyId); + $role = $account->getMeta('accountRole'); + if ('' === $role) { + $role = 'no_account_type'; // @codeCoverageIgnore + } + if (null === $currency) { + $currency = $defaultCurrency; + } + + $key = (string)trans('firefly.opt_group_' . $role); + $grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')'; + } + + return $this->select($name, $grouped, $value, $options); + } + /** * @param string $name * @param null $value @@ -99,7 +141,6 @@ class ExpandedForm /** * @param string $name - * @param $selected * @param null $options * * @return string @@ -183,6 +224,7 @@ class ExpandedForm * * @return string * @throws \FireflyIII\Exceptions\FireflyException + * @throws \Throwable */ public function balance(string $name, $value = null, array $options = []): string { @@ -244,6 +286,33 @@ class ExpandedForm return $res; } + /** + * @param string $name + * @param null $value + * @param array $options + * + * @return string + * @throws \Throwable + */ + public function currencyListEmpty(string $name, $value = null, array $options = []): string + { + /** @var CurrencyRepositoryInterface $currencyRepos */ + $currencyRepos = app(CurrencyRepositoryInterface::class); + + // get all currencies: + $list = $currencyRepos->get(); + $array = [ + 0 => trans('firefly.no_currency'), + ]; + /** @var TransactionCurrency $currency */ + foreach ($list as $currency) { + $array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; + } + $res = $this->select($name, $array, $value, $options); + + return $res; + } + /** * @param string $name * @param null $value @@ -485,16 +554,7 @@ class ExpandedForm */ public function optionsList(string $type, string $name): string { - $previousValue = null; - - try { - $previousValue = request()->old('post_submit_action'); - } catch (RuntimeException $e) { - // don't care - } - - $previousValue = $previousValue ?? 'store'; - $html = view('form.options', compact('type', 'name', 'previousValue'))->render(); + $html = view('form.options', compact('type', 'name'))->render(); return $html; } @@ -506,8 +566,9 @@ class ExpandedForm * @return string * @throws \Throwable */ - public function password(string $name, array $options = []): string + public function password(string $name, array $options = null): string { + $options = $options ?? []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -516,6 +577,32 @@ class ExpandedForm return $html; } + /** + * @param string $name + * @param null $value + * @param array $options + * + * @return string + * @throws \Throwable + */ + public function piggyBankList(string $name, $value = null, array $options = null): string + { + $options = $options ?? []; + // make repositories + /** @var PiggyBankRepositoryInterface $repository */ + $repository = app(PiggyBankRepositoryInterface::class); + $piggyBanks = $repository->getPiggyBanksWithAmount(); + $array = [ + 0 => trans('firefly.none_in_select_list'), + ]; + /** @var PiggyBank $piggy */ + foreach ($piggyBanks as $piggy) { + $array[$piggy->id] = $piggy->name; + } + + return $this->select($name, $array, $value, $options); + } + /** * @param string $name * @param null $value @@ -773,10 +860,13 @@ class ExpandedForm $key = 'amount_currency_id_' . $name; $sentCurrencyId = isset($preFilled[$key]) ? (int)$preFilled[$key] : $defaultCurrency->id; + Log::debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); + // find this currency in set of currencies: foreach ($currencies as $currency) { if ($currency->id === $sentCurrencyId) { $defaultCurrency = $currency; + Log::debug(sprintf('default currency is now %s', $defaultCurrency->code)); break; } } diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 19b8feca8a..0890e601ad 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -55,7 +55,7 @@ class FireflyConfig * * @return \FireflyIII\Models\Configuration|null */ - public function get($name, $default = null) + public function get($name, $default = null): ?Configuration { $fullName = 'ff-config-' . $name; if (Cache::has($fullName)) { diff --git a/app/Support/Import/Information/GetSpectreCustomerTrait.php b/app/Support/Import/Information/GetSpectreCustomerTrait.php index 62660e7a48..bb3010d3f3 100644 --- a/app/Support/Import/Information/GetSpectreCustomerTrait.php +++ b/app/Support/Import/Information/GetSpectreCustomerTrait.php @@ -51,6 +51,8 @@ trait GetSpectreCustomerTrait /** @var NewCustomerRequest $request */ $request = app(NewCustomerRequest::class); $request->setUser($importJob->user); + $request->call(); + // todo what if customer is still null? $customer = $request->getCustomer(); diff --git a/app/Support/Import/JobConfiguration/Bunq/BunqJobConfigurationInterface.php b/app/Support/Import/JobConfiguration/Bunq/BunqJobConfigurationInterface.php index 3a52f6af99..dc85d3b541 100644 --- a/app/Support/Import/JobConfiguration/Bunq/BunqJobConfigurationInterface.php +++ b/app/Support/Import/JobConfiguration/Bunq/BunqJobConfigurationInterface.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\JobConfiguration\Bunq; + use FireflyIII\Models\ImportJob; use Illuminate\Support\MessageBag; diff --git a/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php b/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php index 247a7a2916..ff8764cb52 100644 --- a/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php +++ b/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php @@ -76,10 +76,11 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface */ public function configureJob(array $data): MessageBag { - $config = $this->repository->getConfiguration($this->importJob); - $accounts = $config['accounts'] ?? []; - $mapping = $data['account_mapping'] ?? []; - $final = []; + $config = $this->repository->getConfiguration($this->importJob); + $accounts = $config['accounts'] ?? []; + $mapping = $data['account_mapping'] ?? []; + $applyRules = (int)$data['apply_rules'] === 1; + $final = []; if (\count($accounts) === 0) { throw new FireflyException('No bunq accounts found. Import cannot continue.'); // @codeCoverageIgnore } @@ -98,7 +99,8 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface $accountId = $this->validLocalAccount($localId); $final[$bunqId] = $accountId; } - $config['mapping'] = $final; + $config['mapping'] = $final; + $config['apply-rules'] = $applyRules; $this->repository->setConfiguration($this->importJob, $config); return new MessageBag; diff --git a/app/Support/Import/JobConfiguration/Bunq/NewBunqJobHandler.php b/app/Support/Import/JobConfiguration/Bunq/NewBunqJobHandler.php index 2f3f208c93..e83d4247b5 100644 --- a/app/Support/Import/JobConfiguration/Bunq/NewBunqJobHandler.php +++ b/app/Support/Import/JobConfiguration/Bunq/NewBunqJobHandler.php @@ -45,11 +45,6 @@ class NewBunqJobHandler implements BunqJobConfigurationInterface */ public function configurationComplete(): bool { - // simply set the job configuration "apply-rules" to true. - $config = $this->repository->getConfiguration($this->importJob); - $config['apply-rules'] = true; - $this->repository->setConfiguration($this->importJob, $config); - return true; } diff --git a/app/Support/Import/JobConfiguration/File/ConfigureUploadHandler.php b/app/Support/Import/JobConfiguration/File/ConfigureUploadHandler.php index 144d472f86..76962d0132 100644 --- a/app/Support/Import/JobConfiguration/File/ConfigureUploadHandler.php +++ b/app/Support/Import/JobConfiguration/File/ConfigureUploadHandler.php @@ -35,61 +35,12 @@ use Log; */ class ConfigureUploadHandler implements FileConfigurationInterface { - /** @var ImportJob */ - private $importJob; - - /** @var ImportJobRepositoryInterface */ - private $repository; - /** @var AccountRepositoryInterface */ private $accountRepos; - - /** - * Get the data necessary to show the configuration screen. - * - * @return array - */ - public function getNextData(): array - { - $delimiters = [ - ',' => trans('form.csv_comma'), - ';' => trans('form.csv_semicolon'), - 'tab' => trans('form.csv_tab'), - ]; - $config = $this->importJob->configuration; - $config['date-format'] = $config['date-format'] ?? 'Ymd'; - $specifics = []; - $this->repository->setConfiguration($this->importJob, $config); - - // collect specifics. - foreach (config('csv.import_specifics') as $name => $className) { - $specifics[$name] = [ - 'name' => trans($className::getName()), - 'description' => trans($className::getDescription()), - ]; - } - - $data = [ - 'accounts' => [], - 'delimiters' => $delimiters, - 'specifics' => $specifics, - ]; - - return $data; - } - - /** - * @param ImportJob $importJob - */ - public function setImportJob(ImportJob $importJob): void - { - $this->importJob = $importJob; - $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($importJob->user); - $this->accountRepos = app(AccountRepositoryInterface::class); - $this->accountRepos->setUser($importJob->user); - - } + /** @var ImportJob */ + private $importJob; + /** @var ImportJobRepositoryInterface */ + private $repository; /** * Store data associated with current stage. @@ -137,6 +88,40 @@ class ConfigureUploadHandler implements FileConfigurationInterface return new MessageBag; } + /** + * Get the data necessary to show the configuration screen. + * + * @return array + */ + public function getNextData(): array + { + $delimiters = [ + ',' => trans('form.csv_comma'), + ';' => trans('form.csv_semicolon'), + 'tab' => trans('form.csv_tab'), + ]; + $config = $this->importJob->configuration; + $config['date-format'] = $config['date-format'] ?? 'Ymd'; + $specifics = []; + $this->repository->setConfiguration($this->importJob, $config); + + // collect specifics. + foreach (config('csv.import_specifics') as $name => $className) { + $specifics[$name] = [ + 'name' => trans($className::getName()), + 'description' => trans($className::getDescription()), + ]; + } + + $data = [ + 'accounts' => [], + 'delimiters' => $delimiters, + 'specifics' => $specifics, + ]; + + return $data; + } + /** * @param array $data * @@ -159,4 +144,17 @@ class ConfigureUploadHandler implements FileConfigurationInterface return $return; } + + /** + * @param ImportJob $importJob + */ + public function setImportJob(ImportJob $importJob): void + { + $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->repository->setUser($importJob->user); + $this->accountRepos = app(AccountRepositoryInterface::class); + $this->accountRepos->setUser($importJob->user); + + } } diff --git a/app/Support/Import/JobConfiguration/File/NewFileJobHandler.php b/app/Support/Import/JobConfiguration/File/NewFileJobHandler.php index 27ba527bc2..40c9ac8888 100644 --- a/app/Support/Import/JobConfiguration/File/NewFileJobHandler.php +++ b/app/Support/Import/JobConfiguration/File/NewFileJobHandler.php @@ -80,6 +80,7 @@ class NewFileJobHandler implements FileConfigurationInterface /** * * Get the data necessary to show the configuration screen. + * * @codeCoverageIgnore * @return array */ diff --git a/app/Support/Import/JobConfiguration/Spectre/ChooseAccountsHandler.php b/app/Support/Import/JobConfiguration/Spectre/ChooseAccountsHandler.php index ee85ebfb8d..9797acd428 100644 --- a/app/Support/Import/JobConfiguration/Spectre/ChooseAccountsHandler.php +++ b/app/Support/Import/JobConfiguration/Spectre/ChooseAccountsHandler.php @@ -83,9 +83,10 @@ class ChooseAccountsHandler implements SpectreJobConfigurationInterface public function configureJob(array $data): MessageBag { Log::debug('Now in ChooseAccountsHandler::configureJob()', $data); - $config = $this->importJob->configuration; - $mapping = $data['account_mapping'] ?? []; - $final = []; + $config = $this->importJob->configuration; + $mapping = $data['account_mapping'] ?? []; + $final = []; + $applyRules = (int)$data['apply_rules'] === 1; foreach ($mapping as $spectreId => $localId) { // validate each $spectreId = $this->validSpectreAccount((int)$spectreId); @@ -96,7 +97,7 @@ class ChooseAccountsHandler implements SpectreJobConfigurationInterface Log::debug('Final mapping is:', $final); $messages = new MessageBag; $config['account_mapping'] = $final; - + $config['apply-rules'] = $applyRules; $this->repository->setConfiguration($this->importJob, $config); if ($final === [0 => 0] || \count($final) === 0) { $messages->add('count', trans('import.spectre_no_mapping')); diff --git a/app/Support/Import/JobConfiguration/Spectre/ChooseLoginHandler.php b/app/Support/Import/JobConfiguration/Spectre/ChooseLoginHandler.php index 1687a0689d..d4f1a60e73 100644 --- a/app/Support/Import/JobConfiguration/Spectre/ChooseLoginHandler.php +++ b/app/Support/Import/JobConfiguration/Spectre/ChooseLoginHandler.php @@ -26,12 +26,7 @@ namespace FireflyIII\Support\Import\JobConfiguration\Spectre; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Services\Spectre\Object\Customer; use FireflyIII\Services\Spectre\Object\Login; -use FireflyIII\Services\Spectre\Object\Token; -use FireflyIII\Services\Spectre\Request\CreateTokenRequest; -use FireflyIII\Services\Spectre\Request\ListCustomersRequest; -use FireflyIII\Services\Spectre\Request\NewCustomerRequest; use FireflyIII\Support\Import\Information\GetSpectreCustomerTrait; use FireflyIII\Support\Import\Information\GetSpectreTokenTrait; use Illuminate\Support\MessageBag; diff --git a/app/Support/Import/Placeholder/ColumnValue.php b/app/Support/Import/Placeholder/ColumnValue.php index edefbcc168..b55cd00a67 100644 --- a/app/Support/Import/Placeholder/ColumnValue.php +++ b/app/Support/Import/Placeholder/ColumnValue.php @@ -25,6 +25,7 @@ namespace FireflyIII\Support\Import\Placeholder; /** * Class ColumnValue + * * @codeCoverageIgnore */ class ColumnValue diff --git a/app/Support/Import/Placeholder/ImportTransaction.php b/app/Support/Import/Placeholder/ImportTransaction.php index a9ce98af63..bdda187930 100644 --- a/app/Support/Import/Placeholder/ImportTransaction.php +++ b/app/Support/Import/Placeholder/ImportTransaction.php @@ -179,7 +179,9 @@ class ImportTransaction $this->budgetName = $columnValue->getValue(); break; case 'category-id': - $this->categoryId = $this->getMappedValue($columnValue); + $value = $this->getMappedValue($columnValue); + Log::debug(sprintf('Set category ID to %d in ImportTransaction object', $value)); + $this->categoryId = $value; break; case 'category-name': $this->categoryName = $columnValue->getValue(); diff --git a/app/Support/Import/Routine/File/CSVProcessor.php b/app/Support/Import/Routine/File/CSVProcessor.php index 0d338f0fb4..e5b295af2a 100644 --- a/app/Support/Import/Routine/File/CSVProcessor.php +++ b/app/Support/Import/Routine/File/CSVProcessor.php @@ -62,7 +62,7 @@ class CSVProcessor implements FileProcessorInterface // validate mapped values: /** @var MappedValuesValidator $validator */ - $validator = app(MappedValuesValidator::class); + $validator = app(MappedValuesValidator::class); $validator->setImportJob($this->importJob); $mappedValues = $validator->validate($mappingConverger->getMappedValues()); diff --git a/app/Support/Import/Routine/File/CurrencyMapper.php b/app/Support/Import/Routine/File/CurrencyMapper.php index 2dd366d4d6..b2a45500f3 100644 --- a/app/Support/Import/Routine/File/CurrencyMapper.php +++ b/app/Support/Import/Routine/File/CurrencyMapper.php @@ -71,10 +71,13 @@ class CurrencyMapper return $result; } } + if (!isset($data['code']) || $data['code'] === null) { + return null; + } + // if still nothing, and fields not null, try to create it - $code = $data['code'] ?? null; $creation = [ - 'code' => $code, + 'code' => $data['code'], 'name' => $data['name'] ?? $code, 'symbol' => $data['symbol'] ?? $code, 'decimal_places' => 2, diff --git a/app/Support/Import/Routine/File/FileProcessorInterface.php b/app/Support/Import/Routine/File/FileProcessorInterface.php index c361dd9a44..13f6dfffd8 100644 --- a/app/Support/Import/Routine/File/FileProcessorInterface.php +++ b/app/Support/Import/Routine/File/FileProcessorInterface.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\Routine\File; + use FireflyIII\Models\ImportJob; diff --git a/app/Support/Import/Routine/File/ImportableConverter.php b/app/Support/Import/Routine/File/ImportableConverter.php index 5084f24743..bb851e24e4 100644 --- a/app/Support/Import/Routine/File/ImportableConverter.php +++ b/app/Support/Import/Routine/File/ImportableConverter.php @@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Placeholder\ImportTransaction; use InvalidArgumentException; @@ -39,6 +40,8 @@ use Log; */ class ImportableConverter { + /** @var AccountRepositoryInterface */ + private $accountRepository; /** @var AssetAccountMapper */ private $assetMapper; /** @var array */ @@ -102,6 +105,10 @@ class ImportableConverter $this->assetMapper->setUser($importJob->user); $this->assetMapper->setDefaultAccount($this->config['import-account'] ?? 0); + // asset account repository is used for currency information + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository->setUser($importJob->user); + // opposing account mapper: $this->opposingMapper = app(OpposingAccountMapper::class); $this->opposingMapper->setUser($importJob->user); @@ -122,6 +129,28 @@ class ImportableConverter $this->mappedValues = $mappedValues; } + /** + * @param string|null $date + * + * @return string|null + */ + private function convertDateValue(string $date = null): ?string + { + if (null === $date) { + return null; + } + try { + $object = Carbon::createFromFormat($this->config['date-format'] ?? 'Ymd', $date); + } catch (InvalidDateException|InvalidArgumentException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + + return null; + } + + return $object->format('Y-m-d'); + } + /** * @param ImportTransaction $importable * @@ -154,14 +183,17 @@ class ImportableConverter $currency = $this->currencyMapper->map($currencyId, $importable->getCurrencyData()); $foreignCurrency = $this->currencyMapper->map($foreignCurrencyId, $importable->getForeignCurrencyData()); - if (null === $currency) { - Log::debug(sprintf('Could not map currency, use default (%s)', $this->defaultCurrency->code)); - $currency = $this->defaultCurrency; - } Log::debug(sprintf('"%s" (#%d) is source and "%s" (#%d) is destination.', $source->name, $source->id, $destination->name, $destination->id)); - if (bccomp($amount, '0') === 1) { - // amount is positive? Then switch: + + if ($source->accountType->type === AccountType::ASSET && $destination->accountType->type === AccountType::ASSET) { + Log::debug('Source and destination are asset accounts. This is a transfer.'); + $transactionType = 'transfer'; + } + + // amount is positive and its not a transfer? Then switch: + if ($transactionType !== 'transfer' && bccomp($amount, '0') === 1) { + [$destination, $source] = [$source, $destination]; Log::debug( sprintf( @@ -170,11 +202,41 @@ class ImportableConverter ) ); } - - if ($source->accountType->type === AccountType::ASSET && $destination->accountType->type === AccountType::ASSET) { - Log::debug('Source and destination are asset accounts. This is a transfer.'); - $transactionType = 'transfer'; + // amount is negative and type is transfer? then switch. + if ($transactionType === 'transfer' && bccomp($amount, '0') === -1) { + // amount is positive? Then switch: + [$destination, $source] = [$source, $destination]; + Log::debug( + sprintf( + '%s is negative, so "%s" (#%d) is now source and "%s" (#%d) is now destination.', + $amount, $source->name, $source->id, $destination->name, $destination->id + ) + ); } + + // get currency preference from source asset account (preferred) + // or destination asset account + if (null === $currency) { + if ($destination->accountType->type === AccountType::ASSET) { + // destination is asset, might have currency preference: + $destinationCurrencyId = (int)$this->accountRepository->getMetaValue($destination, 'currency_id'); + $currency = $destinationCurrencyId === 0 ? $this->defaultCurrency : $this->currencyMapper->map($destinationCurrencyId, []); + Log::debug(sprintf('Destination is an asset account, and has currency preference %s', $currency->code)); + } + + if ($source->accountType->type === AccountType::ASSET) { + // source is asset, might have currency preference: + $sourceCurrencyId = (int)$this->accountRepository->getMetaValue($source, 'currency_id'); + $currency = $sourceCurrencyId === 0 ? $this->defaultCurrency : $this->currencyMapper->map($sourceCurrencyId, []); + Log::debug(sprintf('Source is an asset account, and has currency preference %s', $currency->code)); + } + } + if (null === $currency) { + Log::debug(sprintf('Could not map currency, use default (%s)', $this->defaultCurrency->code)); + $currency = $this->defaultCurrency; + } + + if ($source->accountType->type === AccountType::REVENUE) { Log::debug('Source is a revenue account. This is a deposit.'); $transactionType = 'deposit'; @@ -199,19 +261,13 @@ class ImportableConverter throw new FireflyException($message); } - // throw error when both are he same - - try { - $date = Carbon::createFromFormat($this->config['date-format'] ?? 'Ymd', $importable->date); - } catch (InvalidDateException|InvalidArgumentException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - $date = new Carbon; + $dateStr = $this->convertDateValue($importable->date); + if (null === $dateStr) { + $date = new Carbon; + $dateStr = $date->format('Y-m-d'); + unset($date); } - - $dateStr = $date->format('Y-m-d'); - return [ 'type' => $transactionType, 'date' => $dateStr, @@ -228,12 +284,12 @@ class ImportableConverter 'sepa-country' => $importable->meta['sepa-countru'] ?? null, 'sepa-ep' => $importable->meta['sepa-ep'] ?? null, 'sepa-ci' => $importable->meta['sepa-ci'] ?? null, - 'interest_date' => $importable->meta['date-interest'] ?? null, - 'book_date' => $importable->meta['date-book'] ?? null, - 'process_date' => $importable->meta['date-process'] ?? null, - 'due_date' => $importable->meta['date-due'] ?? null, - 'payment_date' => $importable->meta['date-payment'] ?? null, - 'invoice_date' => $importable->meta['date-invoice'] ?? null, + 'interest_date' => $this->convertDateValue($importable->meta['date-interest'] ?? null), + 'book_date' => $this->convertDateValue($importable->meta['date-book'] ?? null), + 'process_date' => $this->convertDateValue($importable->meta['date-process'] ?? null), + 'due_date' => $this->convertDateValue($importable->meta['date-due'] ?? null), + 'payment_date' => $this->convertDateValue($importable->meta['date-payment'] ?? null), + 'invoice_date' => $this->convertDateValue($importable->meta['date-invoice'] ?? null), 'external_id' => $importable->externalId, // journal data: @@ -241,7 +297,7 @@ class ImportableConverter 'piggy_bank_id' => null, 'piggy_bank_name' => null, 'bill_id' => $billId, - 'bill_name' => null === $billId ? $importable->billName : null, + 'bill_name' => $importable->billName, // transaction data: 'transactions' => [ @@ -279,11 +335,16 @@ class ImportableConverter */ private function verifyObjectId(string $key, int $objectId): ?int { + if (isset($this->mappedValues[$key]) && \in_array($objectId, $this->mappedValues[$key], true)) { + Log::debug(sprintf('verifyObjectId(%s, %d) is valid!', $key, $objectId)); + return $objectId; } - return null; + Log::debug(sprintf('verifyObjectId(%s, %d) is NOT in the list, but it could still be valid.', $key, $objectId)); + + return $objectId; } diff --git a/app/Support/Import/Routine/File/LineReader.php b/app/Support/Import/Routine/File/LineReader.php index 36744c5d38..927847313b 100644 --- a/app/Support/Import/Routine/File/LineReader.php +++ b/app/Support/Import/Routine/File/LineReader.php @@ -164,6 +164,7 @@ class LineReader } catch (\League\Csv\Exception $e) { throw new FireflyException(sprintf('Cannot set delimiter: %s', $e->getMessage())); } + // @codeCoverageIgnoreEnd return $reader; diff --git a/app/Support/Import/Routine/File/MappedValuesValidator.php b/app/Support/Import/Routine/File/MappedValuesValidator.php index 8526b2b1f7..e9229a275a 100644 --- a/app/Support/Import/Routine/File/MappedValuesValidator.php +++ b/app/Support/Import/Routine/File/MappedValuesValidator.php @@ -87,6 +87,7 @@ class MappedValuesValidator $return = []; Log::debug('Now in validateMappedValues()'); foreach ($mappings as $role => $values) { + Log::debug(sprintf('Now at role "%s"', $role)); $values = array_unique($values); if (\count($values) > 0) { switch ($role) { @@ -115,9 +116,11 @@ class MappedValuesValidator $return[$role] = $valid; break; case 'category-id': + Log::debug('Going to validate these category ids: ', $values); $set = $this->catRepos->getByIds($values); $valid = $set->pluck('id')->toArray(); $return[$role] = $valid; + Log::debug('Valid category IDs are: ', $valid); break; } } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 38825077c6..8774c29d6e 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -125,14 +125,14 @@ class Preferences * * @return \FireflyIII\Models\Preference|null */ - public function getForUser(User $user, $name, $default = null) + public function getForUser(User $user, $name, $default = null): ?Preference { $fullName = sprintf('preference%s%s', $user->id, $name); if (Cache::has($fullName)) { return Cache::get($fullName); } - $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); + $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $preference && null === $preference->data) { try { $preference->delete(); @@ -163,14 +163,17 @@ class Preferences { $lastActivity = microtime(); $preference = $this->get('lastActivity', microtime()); + if (null !== $preference && null !== $preference->data) { $lastActivity = $preference->data; } if (\is_array($lastActivity)) { $lastActivity = implode(',', $lastActivity); } + $hash = md5($lastActivity); + Log::debug(sprintf('Value of last activity is %s, hash is %s', $lastActivity, $hash)); - return md5($lastActivity); + return $hash; } /** @@ -208,7 +211,7 @@ class Preferences /** * @param \FireflyIII\User $user * @param $name - * @param mixed $value + * @param mixed $value * * @return Preference */ @@ -216,7 +219,7 @@ class Preferences { $fullName = sprintf('preference%s%s', $user->id, $name); Cache::forget($fullName); - $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); + $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $pref) { $pref->data = $value; diff --git a/app/Support/Steam.php b/app/Support/Steam.php index e3ab8d1d72..6c6003e6d2 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -55,7 +55,7 @@ class Steam // /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $currencyId = (int)$repository->getMetaValue($account, 'currency_id'); + $currencyId = (int)$repository->getMetaValue($account, 'currency_id'); // use system default currency: if (0 === $currencyId) { @@ -77,9 +77,9 @@ class Steam ->where('transactions.transaction_currency_id', '!=', $currencyId) ->sum('transactions.foreign_amount'); - $balance = bcadd($nativeBalance, $foreignBalance); - $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; - $balance = bcadd($balance, $virtual); + $balance = bcadd($nativeBalance, $foreignBalance); + $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; + $balance = bcadd($balance, $virtual); $cache->store($balance); return $balance; diff --git a/app/Support/Twig/Extension/Transaction.php b/app/Support/Twig/Extension/Transaction.php index 759880ff88..5294840ac7 100644 --- a/app/Support/Twig/Extension/Transaction.php +++ b/app/Support/Twig/Extension/Transaction.php @@ -26,11 +26,11 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Attachment; use FireflyIII\Models\Transaction as TransactionModel; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use Lang; use Log; use Twig_Extension; -use FireflyIII\Models\TransactionJournal; /** * Class Transaction. diff --git a/app/Support/Twig/Extension/TransactionJournal.php b/app/Support/Twig/Extension/TransactionJournal.php index c9b56a3d8b..66f05708d3 100644 --- a/app/Support/Twig/Extension/TransactionJournal.php +++ b/app/Support/Twig/Extension/TransactionJournal.php @@ -96,10 +96,51 @@ class TransactionJournal extends Twig_Extension * @return string */ public function totalAmount(JournalModel $journal): string + { + $type = $journal->transactionType->type; + $totals = $this->getTotalAmount($journal); + $array = []; + foreach ($totals as $total) { + if (TransactionType::WITHDRAWAL === $type) { + $total['amount'] = bcmul($total['amount'], '-1'); + } + $array[] = app('amount')->formatAnything($total['currency'], $total['amount']); + } + + return implode(' / ', $array); + } + + /** + * @param JournalModel $journal + * + * @return string + */ + public function totalAmountPlain(JournalModel $journal): string + { + $type = $journal->transactionType->type; + $totals = $this->getTotalAmount($journal); + $array = []; + + foreach ($totals as $total) { + if (TransactionType::WITHDRAWAL === $type) { + $total['amount'] = bcmul($total['amount'], '-1'); + } + $array[] = app('amount')->formatAnything($total['currency'], $total['amount'], false); + } + + return implode(' / ', $array); + } + + /** + * @param JournalModel $journal + * + * @return string + */ + private function getTotalAmount(JournalModel $journal): array { $transactions = $journal->transactions()->where('amount', '>', 0)->get(); $totals = []; - $type = $journal->transactionType->type; + /** @var TransactionModel $transaction */ foreach ($transactions as $transaction) { $currencyId = $transaction->transaction_currency_id; @@ -128,14 +169,7 @@ class TransactionJournal extends Twig_Extension ); } } - $array = []; - foreach ($totals as $total) { - if (TransactionType::WITHDRAWAL === $type) { - $total['amount'] = bcmul($total['amount'], '-1'); - } - $array[] = app('amount')->formatAnything($total['currency'], $total['amount']); - } - return implode(' / ', $array); + return $totals; } } diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index a2945e833d..e744976414 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -24,12 +24,12 @@ namespace FireflyIII\Support\Twig; use Carbon\Carbon; use FireflyIII\Models\Account; +use FireflyIII\Support\Twig\Extension\Account as AccountExtension; use League\CommonMark\CommonMarkConverter; use Route; use Twig_Extension; use Twig_SimpleFilter; use Twig_SimpleFunction; -use FireflyIII\Support\Twig\Extension\Account as AccountExtension; /** * Class TwigSupport. diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index c68a7bedfe..c6502a8f72 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -79,6 +79,7 @@ class Journal extends Twig_Extension { $filters = [ new Twig_SimpleFilter('journalTotalAmount', [TransactionJournalExtension::class, 'totalAmount'], ['is_safe' => ['html']]), + new Twig_SimpleFilter('journalTotalAmountPlain', [TransactionJournalExtension::class, 'totalAmountPlain'], ['is_safe' => ['html']]), ]; return $filters; @@ -198,6 +199,7 @@ class Journal extends Twig_Extension ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->where('categories.user_id', $journal->user_id) ->where('transaction_journals.id', $journal->id) + ->whereNull('transactions.deleted_at') ->get(['categories.*']); /** @var Category $category */ foreach ($set as $category) { diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 6ffb84cb8e..d0bd43240c 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -58,8 +58,8 @@ class LinkToBill implements ActionInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($this->action->rule->user); - $billName = (string)$this->action->action_value; - $bill = $repository->findByName($billName); + $billName = (string)$this->action->action_value; + $bill = $repository->findByName($billName); if (null !== $bill && $journal->transactionType->type === TransactionType::WITHDRAWAL) { $journal->bill()->associate($bill); diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 1ad5fb1a46..956ae71670 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -101,8 +101,8 @@ class SetDestinationAccount implements ActionInterface // update destination transaction with new destination account: // get destination transaction: - $transaction = $journal->transactions()->where('amount', '>', 0)->first(); - if(null === $transaction) { + $transaction = $journal->transactions()->where('amount', '>', 0)->first(); + if (null === $transaction) { return true; } $transaction->account_id = $this->newDestinationAccount->id; diff --git a/app/TransactionRules/Triggers/HasAnyBudget.php b/app/TransactionRules/Triggers/HasAnyBudget.php index 46bd011a64..224c4b9212 100644 --- a/app/TransactionRules/Triggers/HasAnyBudget.php +++ b/app/TransactionRules/Triggers/HasAnyBudget.php @@ -81,7 +81,7 @@ final class HasAnyBudget extends AbstractTrigger implements TriggerInterface } } - Log::debug(sprintf('RuleTrigger HasAnyBudget for journal #%d: count is %d, return false.', $journal->id, $count)); + Log::debug(sprintf('RuleTrigger HasAnyBudget for journal #%d: final is false.', $journal->id)); return false; } diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 099398fd8c..64ec626911 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -132,7 +132,7 @@ class AccountTransformer extends TransformerAbstract */ public function includeUser(Account $account): Item { - return $this->item($account->user, new UserTransformer($this->parameters), 'user'); + return $this->item($account->user, new UserTransformer($this->parameters), 'users'); } /** @@ -153,7 +153,7 @@ class AccountTransformer extends TransformerAbstract } $currencyId = (int)$this->repository->getMetaValue($account, 'currency_id'); $currencyCode = null; - $currencySymbol = 'x'; + $currencySymbol = null; $decimalPlaces = 2; if ($currencyId > 0) { $currency = TransactionCurrency::find($currencyId); diff --git a/app/Transformers/AttachmentTransformer.php b/app/Transformers/AttachmentTransformer.php index 0db1993352..d65d4db907 100644 --- a/app/Transformers/AttachmentTransformer.php +++ b/app/Transformers/AttachmentTransformer.php @@ -25,6 +25,7 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\Attachment; +use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\TransformerAbstract; use Symfony\Component\HttpFoundation\ParameterBag; @@ -39,13 +40,13 @@ class AttachmentTransformer extends TransformerAbstract * * @var array */ - protected $availableIncludes = ['user']; + protected $availableIncludes = ['user', 'notes']; /** * List of resources to automatically include * * @var array */ - protected $defaultIncludes = ['user']; + protected $defaultIncludes = ['user', 'notes']; /** @var ParameterBag */ protected $parameters; @@ -62,6 +63,20 @@ class AttachmentTransformer extends TransformerAbstract $this->parameters = $parameters; } + /** + * Attach the notes. + * + * @codeCoverageIgnore + * + * @param Attachment $attachment + * + * @return FractalCollection + */ + public function includeNotes(Attachment $attachment): FractalCollection + { + return $this->collection($attachment->notes, new NoteTransformer($this->parameters), 'notes'); + } + /** * Attach the user. * @@ -73,7 +88,7 @@ class AttachmentTransformer extends TransformerAbstract */ public function includeUser(Attachment $attachment): Item { - return $this->item($attachment->user, new UserTransformer($this->parameters), 'user'); + return $this->item($attachment->user, new UserTransformer($this->parameters), 'users'); } /** @@ -92,11 +107,11 @@ class AttachmentTransformer extends TransformerAbstract 'attachable_type' => $attachment->attachable_type, 'md5' => $attachment->md5, 'filename' => $attachment->filename, + 'download_uri' => route('api.v1.attachments.download', [$attachment->id]), + 'upload_uri' => route('api.v1.attachments.upload', [$attachment->id]), 'title' => $attachment->title, - 'description' => $attachment->description, - 'notes' => $attachment->notes, 'mime' => $attachment->mime, - 'size' => $attachment->size, + 'size' => (int)$attachment->size, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php new file mode 100644 index 0000000000..e50d0a162f --- /dev/null +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -0,0 +1,120 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\AvailableBudget; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class AvailableBudgetTransformer + */ +class AvailableBudgetTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['transaction_currency', 'user']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['transaction_currency']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Attach the currency. + * + * @codeCoverageIgnore + * + * @param AvailableBudget $availableBudget + * + * @return Item + */ + public function includeTransactionCurrency(AvailableBudget $availableBudget): Item + { + return $this->item($availableBudget->transactionCurrency, new CurrencyTransformer($this->parameters), 'transaction_currencies'); + } + + /** + * Attach the user. + * + * @codeCoverageIgnore + * + * @param AvailableBudget $availableBudget + * + * @return Item + */ + public function includeUser(AvailableBudget $availableBudget): Item + { + return $this->item($availableBudget->user, new UserTransformer($this->parameters), 'users'); + } + + /** + * Transform the note. + * + * @param AvailableBudget $availableBudget + * + * @return array + */ + public function transform(AvailableBudget $availableBudget): array + { + $data = [ + 'id' => (int)$availableBudget->id, + 'updated_at' => $availableBudget->updated_at->toAtomString(), + 'created_at' => $availableBudget->created_at->toAtomString(), + 'start_date' => $availableBudget->start_date->format('Y-m-d'), + 'end_date' => $availableBudget->end_date->format('Y-m-d'), + 'amount' => round($availableBudget->amount, $availableBudget->transactionCurrency->decimal_places), + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/available_budgets/' . $availableBudget->id, + ], + ], + ]; + + return $data; + } + +} \ No newline at end of file diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index 78a17fe43d..86c7516833 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -26,7 +26,6 @@ namespace FireflyIII\Transformers; use Carbon\Carbon; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\Bill; -use FireflyIII\Models\Note; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Illuminate\Support\Collection; use League\Fractal\Resource\Collection as FractalCollection; @@ -45,13 +44,13 @@ class BillTransformer extends TransformerAbstract * * @var array */ - protected $availableIncludes = ['attachments', 'transactions', 'user']; + protected $availableIncludes = ['attachments', 'transactions', 'user', 'notes', 'rules']; /** * List of resources to automatically include * * @var array */ - protected $defaultIncludes = []; + protected $defaultIncludes = ['notes', 'rules']; /** @var ParameterBag */ protected $parameters; @@ -83,6 +82,40 @@ class BillTransformer extends TransformerAbstract return $this->collection($attachments, new AttachmentTransformer($this->parameters), 'attachments'); } + /** + * Attach the notes. + * + * @codeCoverageIgnore + * + * @param Bill $bill + * + * @return FractalCollection + */ + public function includeNotes(Bill $bill): FractalCollection + { + return $this->collection($bill->notes, new NoteTransformer($this->parameters), 'notes'); + } + + /** + * Attach the rules. + * + * @codeCoverageIgnore + * + * @param Bill $bill + * + * @return FractalCollection + */ + public function includeRules(Bill $bill): FractalCollection + { + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $repository->setUser($bill->user); + // add info about rules: + $rules = $repository->getRulesForBill($bill); + + return $this->collection($rules, new RuleTransformer($this->parameters), 'rules'); + } + /** * Include any transactions. * @@ -141,19 +174,17 @@ class BillTransformer extends TransformerAbstract 'name' => $bill->name, 'currency_id' => $bill->transaction_currency_id, 'currency_code' => $bill->transactionCurrency->code, - 'match' => explode(',', $bill->match), 'amount_min' => round($bill->amount_min, 2), 'amount_max' => round($bill->amount_max, 2), 'date' => $bill->date->format('Y-m-d'), 'repeat_freq' => $bill->repeat_freq, 'skip' => (int)$bill->skip, - 'automatch' => (int)$bill->automatch === 1, - 'active' => (int)$bill->active === 1, + 'automatch' => $bill->automatch, + 'active' => $bill->active, 'attachments_count' => $bill->attachments()->count(), 'pay_dates' => $payDates, 'paid_dates' => $paidData['paid_dates'], 'next_expected_match' => $paidData['next_expected_match'], - 'notes' => null, 'links' => [ [ 'rel' => 'self', @@ -161,11 +192,6 @@ class BillTransformer extends TransformerAbstract ], ], ]; - /** @var Note $note */ - $note = $bill->notes()->first(); - if (null !== $note) { - $data['notes'] = $note->text; - } return $data; diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php new file mode 100644 index 0000000000..90e858bcdc --- /dev/null +++ b/app/Transformers/BudgetLimitTransformer.php @@ -0,0 +1,104 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + +use FireflyIII\Models\BudgetLimit; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class BudgetLimitTransformer + */ +class BudgetLimitTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['budget']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['budget']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Attach the budget. + * + * @codeCoverageIgnore + * + * @param BudgetLimit $budgetLimit + * + * @return Item + */ + public function includeBudget(BudgetLimit $budgetLimit): Item + { + return $this->item($budgetLimit->budget, new BudgetTransformer($this->parameters), 'budgets'); + } + + /** + * Transform the note. + * + * @param BudgetLimit $budgetLimit + * + * @return array + */ + public function transform(BudgetLimit $budgetLimit): array + { + $data = [ + 'id' => (int)$budgetLimit->id, + 'updated_at' => $budgetLimit->updated_at->toAtomString(), + 'created_at' => $budgetLimit->created_at->toAtomString(), + 'start_date' => $budgetLimit->start_date->format('Y-m-d'), + 'end_date' => $budgetLimit->end_date->format('Y-m-d'), + 'amount' => $budgetLimit->amount, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/budget_limits/' . $budgetLimit->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index 698c3009b8..4b9562292a 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -48,7 +48,7 @@ class BudgetTransformer extends TransformerAbstract * * @var array */ - protected $defaultIncludes = []; + protected $defaultIncludes = ['user']; /** @var ParameterBag */ protected $parameters; diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index 3d7de337c9..421946e6b2 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -48,7 +48,7 @@ class CategoryTransformer extends TransformerAbstract * * @var array */ - protected $defaultIncludes = []; + protected $defaultIncludes = ['user']; /** @var ParameterBag */ protected $parameters; diff --git a/app/Transformers/CurrencyExchangeRateTransformer.php b/app/Transformers/CurrencyExchangeRateTransformer.php new file mode 100644 index 0000000000..228ef4dbbb --- /dev/null +++ b/app/Transformers/CurrencyExchangeRateTransformer.php @@ -0,0 +1,99 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\CurrencyExchangeRate; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +class CurrencyExchangeRateTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['from_currency', 'to_currency']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['from_currency', 'to_currency']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * PiggyBankEventTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * @param CurrencyExchangeRate $rate + * + * @return Item + */ + public function includeFromCurrency(CurrencyExchangeRate $rate): Item + { + return $this->item($rate->fromCurrency, new CurrencyTransformer($this->parameters), 'transaction_currencies'); + } + + /** + * @param CurrencyExchangeRate $rate + * + * @return \League\Fractal\Resource\Item + */ + public function includeToCurrency(CurrencyExchangeRate $rate): Item + { + return $this->item($rate->toCurrency, new CurrencyTransformer($this->parameters), 'transaction_currencies'); + } + + public function transform(CurrencyExchangeRate $rate): array + { + $data = [ + 'id' => (int)$rate->id, + 'updated_at' => $rate->updated_at->toAtomString(), + 'created_at' => $rate->created_at->toAtomString(), + 'rate' => (float)$rate->rate, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/currency_exchange_rates/' . $rate->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/JournalLinkTransformer.php b/app/Transformers/JournalLinkTransformer.php new file mode 100644 index 0000000000..944c9f9b20 --- /dev/null +++ b/app/Transformers/JournalLinkTransformer.php @@ -0,0 +1,146 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Models\Note; +use FireflyIII\Models\TransactionJournalLink; +use Illuminate\Support\Collection; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * + * Class JournalLinkTransformer + */ +class JournalLinkTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['inward', 'outward', 'link_type']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['inward', 'outward', 'link_type']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * @param TransactionJournalLink $link + * + * @return Item + */ + public function includeInward(TransactionJournalLink $link): Item + { + // need to use the collector to get the transaction :( + // journals always use collector and limited using URL parameters. + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser($link->source->user); + $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); + $collector->setJournals(new Collection([$link->source])); + $transactions = $collector->getJournals(); + + return $this->item($transactions->first(), new TransactionTransformer($this->parameters), 'transactions'); + } + + /** + * @param TransactionJournalLink $link + * + * @return Item + */ + public function includeLinkType(TransactionJournalLink $link): Item + { + return $this->item($link->linkType, new LinkTypeTransformer($this->parameters), 'link_types'); + } + + /** + * @param TransactionJournalLink $link + * + * @return Item + */ + public function includeOutward(TransactionJournalLink $link): Item + { + // need to use the collector to get the transaction :( + // journals always use collector and limited using URL parameters. + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setUser($link->source->user); + $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); + $collector->setJournals(new Collection([$link->destination])); + $transactions = $collector->getJournals(); + + return $this->item($transactions->first(), new TransactionTransformer($this->parameters), 'transactions'); + } + + /** + * @param TransactionJournalLink $link + * + * @return array + */ + public function transform(TransactionJournalLink $link): array + { + $notes = ''; + /** @var Note $note */ + $note = $link->notes()->first(); + if (null !== $note) { + $notes = $note->text; + } + + $data = [ + 'id' => (int)$link->id, + 'updated_at' => $link->updated_at->toAtomString(), + 'created_at' => $link->created_at->toAtomString(), + 'notes' => $notes, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/journal_links/' . $link->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/LinkTypeTransformer.php b/app/Transformers/LinkTypeTransformer.php new file mode 100644 index 0000000000..625d390c09 --- /dev/null +++ b/app/Transformers/LinkTypeTransformer.php @@ -0,0 +1,89 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\LinkType; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +class LinkTypeTransformer extends TransformerAbstract +{ + + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = []; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the currency. + * + * @param LinkType $linkType + * + * @return array + */ + public function transform(LinkType $linkType): array + { + $data = [ + 'id' => (int)$linkType->id, + 'updated_at' => $linkType->updated_at->toAtomString(), + 'created_at' => $linkType->created_at->toAtomString(), + 'name' => $linkType->name, + 'inward' => $linkType->inward, + 'outward' => $linkType->outward, + 'editable' => (int)$linkType->editable, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/link_types/' . $linkType->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/NoteTransformer.php b/app/Transformers/NoteTransformer.php new file mode 100644 index 0000000000..9cf804c45a --- /dev/null +++ b/app/Transformers/NoteTransformer.php @@ -0,0 +1,92 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\Note; +use League\CommonMark\CommonMarkConverter; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class NoteTransformer + */ +class NoteTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = []; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the note. + * + * @param Note $note + * + * @return array + */ + public function transform(Note $note): array + { + $converter = new CommonMarkConverter; + $data = [ + 'id' => (int)$note->id, + 'updated_at' => $note->updated_at->toAtomString(), + 'created_at' => $note->created_at->toAtomString(), + 'title' => $note->title, + 'text' => $note->text, + 'markdown' => $converter->convertToHtml($note->text), + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/notes/' . $note->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 5afe13a6ca..0816fb0f63 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -166,9 +166,9 @@ class PiggyBankTransformer extends TransformerAbstract 'percentage' => $percentage, 'current_amount' => $currentAmount, 'left_to_save' => round($leftToSave, $decimalPlaces), - 'save_per_month' => $piggyRepos->getSuggestedMonthlyAmount($piggyBank), - 'startdate' => $startDate, - 'targetdate' => $targetDate, + 'save_per_month' => round($piggyRepos->getSuggestedMonthlyAmount($piggyBank), $decimalPlaces), + 'start_date' => $startDate, + 'target_date' => $targetDate, 'order' => (int)$piggyBank->order, 'active' => (int)$piggyBank->active === 1, 'notes' => null, diff --git a/app/Transformers/PreferenceTransformer.php b/app/Transformers/PreferenceTransformer.php new file mode 100644 index 0000000000..76a9d253cb --- /dev/null +++ b/app/Transformers/PreferenceTransformer.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\Preference; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +class PreferenceTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include. + * + * @var array + */ + protected $availableIncludes = ['user']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + /** @var ParameterBag */ + protected $parameters; + + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the preference + * + * @param Preference $preference + * + * @return array + */ + public function transform(Preference $preference): array + { + return [ + 'id' => (int)$preference->id, + 'updated_at' => $preference->updated_at->toAtomString(), + 'created_at' => $preference->created_at->toAtomString(), + 'name' => $preference->name, + 'data' => $preference->data, + ]; + + } + +} \ No newline at end of file diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php new file mode 100644 index 0000000000..f35df38ddd --- /dev/null +++ b/app/Transformers/RecurrenceTransformer.php @@ -0,0 +1,258 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\CategoryFactory; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * + * Class RecurringTransactionTransformer + */ +class RecurrenceTransformer extends TransformerAbstract +{ + /** @noinspection ClassOverridesFieldOfSuperClassInspection */ + /** + * List of resources possible to include. + * + * @var array + */ + protected $availableIncludes = ['user', 'transactions']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + /** @var ParameterBag */ + protected $parameters; + + /** @var RecurringRepositoryInterface */ + protected $repository; + + + public function __construct(ParameterBag $parameters) + { + $this->repository = app(RecurringRepositoryInterface::class); + $this->parameters = $parameters; + } + + /** + * Include user data in end result. + * + * @codeCoverageIgnore + * + * @param Recurrence $recurrence + * + * + * @return Item + */ + public function includeUser(Recurrence $recurrence): Item + { + return $this->item($recurrence->user, new UserTransformer($this->parameters), 'users'); + } + + /** + * Transform the piggy bank. + * + * @param Recurrence $recurrence + * + * @return array + * @throws FireflyException + */ + public function transform(Recurrence $recurrence): array + { + $this->repository->setUser($recurrence->user); + $return = [ + 'id' => (int)$recurrence->id, + 'updated_at' => $recurrence->updated_at->toAtomString(), + 'created_at' => $recurrence->created_at->toAtomString(), + 'transaction_type_id' => $recurrence->transaction_type_id, + 'transaction_type' => $recurrence->transactionType->type, + 'title' => $recurrence->title, + 'description' => $recurrence->description, + 'first_date' => $recurrence->first_date->format('Y-m-d'), + 'latest_date' => null === $recurrence->latest_date ? null : $recurrence->latest_date->format('Y-m-d'), + 'repeat_until' => null === $recurrence->repeat_until ? null : $recurrence->repeat_until->format('Y-m-d'), + 'apply_rules' => $recurrence->apply_rules, + 'active' => $recurrence->active, + 'repetitions' => $recurrence->repetitions, + 'notes' => $this->repository->getNoteText($recurrence), + 'recurrence_repetitions' => [], + 'transactions' => [], + 'meta' => [], + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/recurring/' . $recurrence->id, + ], + ], + ]; + $fromDate = $recurrence->latest_date ?? $recurrence->first_date; + // date in the past? use today: + $today = new Carbon; + $fromDate = $fromDate->lte($today) ? $today : $fromDate; + + /** @var RecurrenceRepetition $repetition */ + foreach ($recurrence->recurrenceRepetitions as $repetition) { + $repetitionArray = [ + 'id' => $repetition->id, + 'updated_at' => $repetition->updated_at->toAtomString(), + 'created_at' => $repetition->created_at->toAtomString(), + 'repetition_type' => $repetition->repetition_type, + 'repetition_moment' => $repetition->repetition_moment, + 'repetition_skip' => (int)$repetition->repetition_skip, + 'weekend' => (int)$repetition->weekend, + 'description' => $this->repository->repetitionDescription($repetition), + 'occurrences' => [], + ]; + + // get the (future) occurrences for this specific type of repetition: + $occurrences = $this->repository->getXOccurrences($repetition, $fromDate, 5); + /** @var Carbon $carbon */ + foreach ($occurrences as $carbon) { + $repetitionArray['occurrences'][] = $carbon->format('Y-m-d'); + } + + $return['recurrence_repetitions'][] = $repetitionArray; + } + unset($repetitionArray); + + // get all transactions: + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transactionArray = [ + 'currency_id' => $transaction->transaction_currency_id, + 'currency_code' => $transaction->transactionCurrency->code, + 'currency_symbol' => $transaction->transactionCurrency->symbol, + 'currency_dp' => $transaction->transactionCurrency->decimal_places, + 'foreign_currency_id' => $transaction->foreign_currency_id, + 'source_id' => $transaction->source_id, + 'source_name' => $transaction->sourceAccount->name, + 'destination_id' => $transaction->destination_id, + 'destination_name' => $transaction->destinationAccount->name, + 'amount' => $transaction->amount, + 'foreign_amount' => $transaction->foreign_amount, + 'description' => $transaction->description, + 'meta' => [], + ]; + if (null !== $transaction->foreign_currency_id) { + $transactionArray['foreign_currency_code'] = $transaction->foreignCurrency->code; + $transactionArray['foreign_currency_symbol'] = $transaction->foreignCurrency->symbol; + $transactionArray['foreign_currency_dp'] = $transaction->foreignCurrency->decimal_places; + } + + // get meta data for each transaction: + /** @var RecurrenceTransactionMeta $transactionMeta */ + foreach ($transaction->recurrenceTransactionMeta as $transactionMeta) { + $transactionMetaArray = [ + 'name' => $transactionMeta->name, + 'value' => $transactionMeta->value, + ]; + switch ($transactionMeta->name) { + default: + throw new FireflyException(sprintf('Recurrence transformer cannot handle transaction meta-field "%s"', $transactionMeta->name)); + case 'category_name': + /** @var CategoryFactory $factory */ + $factory = app(CategoryFactory::class); + $factory->setUser($recurrence->user); + $category = $factory->findOrCreate(null, $transactionMeta->value); + if (null !== $category) { + $transactionMetaArray['category_id'] = $category->id; + $transactionMetaArray['category_name'] = $category->name; + } + break; + case 'budget_id': + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $budget = $repository->findNull((int)$transactionMeta->value); + if (null !== $budget) { + $transactionMetaArray['budget_id'] = $budget->id; + $transactionMetaArray['budget_name'] = $budget->name; + } + break; + } + // store transaction meta data in transaction + $transactionArray['meta'][] = $transactionMetaArray; + } + // store transaction in recurrence array. + $return['transactions'][] = $transactionArray; + } + // get all meta data for recurrence itself + /** @var RecurrenceMeta $recurrenceMeta */ + foreach ($recurrence->recurrenceMeta as $recurrenceMeta) { + $recurrenceMetaArray = [ + 'name' => $recurrenceMeta->name, + 'value' => $recurrenceMeta->value, + ]; + switch ($recurrenceMeta->name) { + default: + throw new FireflyException(sprintf('Recurrence transformer cannot handle meta-field "%s"', $recurrenceMeta->name)); + case 'tags': + $recurrenceMetaArray['tags'] = explode(',', $recurrenceMeta->value); + break; + case 'notes': + break; + case 'bill_id': + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $bill = $repository->find((int)$recurrenceMeta->value); + if (null !== $bill) { + $recurrenceMetaArray['bill_id'] = $bill->id; + $recurrenceMetaArray['bill_name'] = $bill->name; + } + break; + case 'piggy_bank_id': + /** @var PiggyBankRepositoryInterface $repository */ + $repository = app(PiggyBankRepositoryInterface::class); + $piggy = $repository->findNull((int)$recurrenceMeta->value); + if (null !== $piggy) { + $recurrenceMetaArray['piggy_bank_id'] = $piggy->id; + $recurrenceMetaArray['piggy_bank_name'] = $piggy->name; + } + break; + } + // store meta date in recurring array + $return['meta'][] = $recurrenceMetaArray; + + } + + return $return; + } + +} \ No newline at end of file diff --git a/app/Transformers/RuleActionTransformer.php b/app/Transformers/RuleActionTransformer.php new file mode 100644 index 0000000000..b8b664dd49 --- /dev/null +++ b/app/Transformers/RuleActionTransformer.php @@ -0,0 +1,92 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\RuleAction; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class RuleActionTransformer + */ +class RuleActionTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = []; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the rule action. + * + * @param RuleAction $ruleAction + * + * @return array + */ + public function transform(RuleAction $ruleAction): array + { + $data = [ + 'id' => (int)$ruleAction->id, + 'updated_at' => $ruleAction->updated_at->toAtomString(), + 'created_at' => $ruleAction->created_at->toAtomString(), + 'action_type' => $ruleAction->action_type, + 'action_value' => $ruleAction->action_value, + 'order' => $ruleAction->order, + 'active' => $ruleAction->active, + 'stop_processing' => $ruleAction->stop_processing, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/rule_triggers/' . $ruleAction->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/RuleGroupTransformer.php b/app/Transformers/RuleGroupTransformer.php new file mode 100644 index 0000000000..d749f09945 --- /dev/null +++ b/app/Transformers/RuleGroupTransformer.php @@ -0,0 +1,114 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + +use FireflyIII\Models\RuleGroup; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class RuleGroupTransformer + */ +class RuleGroupTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['user']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['user']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + public function includeRules(RuleGroup $ruleGroup): FractalCollection + { + return $this->collection($ruleGroup->rules, new RuleTransformer($this->parameters), 'rules'); + } + + /** + * Include the user. + * + * @param RuleGroup $ruleGroup + * + * @codeCoverageIgnore + * @return Item + */ + public function includeUser(RuleGroup $ruleGroup): Item + { + return $this->item($ruleGroup->user, new UserTransformer($this->parameters), 'users'); + } + + /** + * Transform the rule group + * + * @param RuleGroup $ruleGroup + * + * @return array + */ + public function transform(RuleGroup $ruleGroup): array + { + $data = [ + 'id' => (int)$ruleGroup->id, + 'updated_at' => $ruleGroup->updated_at->toAtomString(), + 'created_at' => $ruleGroup->created_at->toAtomString(), + 'title' => $ruleGroup->title, + 'text' => $ruleGroup->text, + 'order' => $ruleGroup->order, + 'active' => $ruleGroup->active, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/rule_groups/' . $ruleGroup->id, + ], + ], + ]; + + return $data; + } + + +} + + diff --git a/app/Transformers/RuleTransformer.php b/app/Transformers/RuleTransformer.php new file mode 100644 index 0000000000..89f8353aa0 --- /dev/null +++ b/app/Transformers/RuleTransformer.php @@ -0,0 +1,141 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\Rule; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class RuleTransformer + */ +class RuleTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = ['rule_group', 'rule_triggers', 'rule_actions', 'user']; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = ['rule_group', 'rule_triggers', 'rule_actions']; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * @param Rule $rule + * + * @return FractalCollection + */ + public function includeRuleActions(Rule $rule): FractalCollection + { + return $this->collection($rule->ruleActions, new RuleActionTransformer($this->parameters), 'rule_actions'); + } + + /** + * Include the rule group. + * + * @param Rule $rule + * + * @codeCoverageIgnore + * @return Item + */ + public function includeRuleGroup(Rule $rule): Item + { + return $this->item($rule->ruleGroup, new RuleGroupTransformer($this->parameters), 'rule_groups'); + } + + /** + * @param Rule $rule + * + * @return FractalCollection + */ + public function includeRuleTriggers(Rule $rule): FractalCollection + { + return $this->collection($rule->ruleTriggers, new RuleTriggerTransformer($this->parameters), 'rule_triggers'); + } + + /** + * Include the user. + * + * @param Rule $rule + * + * @codeCoverageIgnore + * @return Item + */ + public function includeUser(Rule $rule): Item + { + return $this->item($rule->user, new UserTransformer($this->parameters), 'users'); + } + + /** + * Transform the rule. + * + * @param Rule $rule + * + * @return array + */ + public function transform(Rule $rule): array + { + $data = [ + 'id' => (int)$rule->id, + 'updated_at' => $rule->updated_at->toAtomString(), + 'created_at' => $rule->created_at->toAtomString(), + 'title' => $rule->title, + 'text' => $rule->text, + 'order' => (int)$rule->order, + 'active' => $rule->active, + 'stop_processing' => $rule->stop_processing, + 'strict' => $rule->strict, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/rules/' . $rule->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/Transformers/RuleTriggerTransformer.php b/app/Transformers/RuleTriggerTransformer.php new file mode 100644 index 0000000000..6c51c84bb3 --- /dev/null +++ b/app/Transformers/RuleTriggerTransformer.php @@ -0,0 +1,92 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\RuleTrigger; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class RuleTriggerTransformer + */ +class RuleTriggerTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = []; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the rule trigger. + * + * @param RuleTrigger $ruleTrigger + * + * @return array + */ + public function transform(RuleTrigger $ruleTrigger): array + { + $data = [ + 'id' => (int)$ruleTrigger->id, + 'updated_at' => $ruleTrigger->updated_at->toAtomString(), + 'created_at' => $ruleTrigger->created_at->toAtomString(), + 'trigger_type' => $ruleTrigger->trigger_type, + 'trigger_value' => $ruleTrigger->trigger_value, + 'order' => $ruleTrigger->order, + 'active' => $ruleTrigger->active, + 'stop_processing' => $ruleTrigger->stop_processing, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/rule_triggers/' . $ruleTrigger->id, + ], + ], + ]; + + return $data; + } +} \ No newline at end of file diff --git a/app/User.php b/app/User.php index 3e3150fd13..28525e3ab4 100644 --- a/app/User.php +++ b/app/User.php @@ -25,7 +25,24 @@ declare(strict_types=1); namespace FireflyIII; use FireflyIII\Events\RequestedNewPassword; +use FireflyIII\Models\Account; +use FireflyIII\Models\Attachment; +use FireflyIII\Models\AvailableBudget; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; use FireflyIII\Models\CurrencyExchangeRate; +use FireflyIII\Models\ExportJob; +use FireflyIII\Models\ImportJob; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Models\Preference; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\Role; +use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; @@ -36,26 +53,11 @@ use Laravel\Passport\HasApiTokens; use Log; use Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\Tag; -use FireflyIII\Models\Rule; -use FireflyIII\Models\RuleGroup; -use FireflyIII\Models\Role; -use FireflyIII\Models\Preference; -use FireflyIII\Models\Account; -use FireflyIII\Models\PiggyBank; -use FireflyIII\Models\ImportJob; -use FireflyIII\Models\ExportJob; -use FireflyIII\Models\Category; -use FireflyIII\Models\Budget; -use FireflyIII\Models\Bill; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\Attachment; /** * Class User. - * @property int $id + * + * @property int $id * @property string $email */ class User extends Authenticatable @@ -290,6 +292,17 @@ class User extends Authenticatable return $this->hasMany(Preference::class); } + /** + * @codeCoverageIgnore + * Link to recurring transactions. + * + * @return HasMany + */ + public function recurrences(): HasMany + { + return $this->hasMany(Recurrence::class); + } + /** * @codeCoverageIgnore * Link to roles. diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 1f9571e99d..15dcc33618 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -225,6 +225,8 @@ class FireflyValidator extends Validator } /** + * TODO lots of if-else because of API calls. + * * @param $attribute * * @return bool @@ -234,12 +236,27 @@ class FireflyValidator extends Validator // get the index from a string like "rule-action-value.2". $parts = explode('.', $attribute); $index = $parts[\count($parts) - 1]; + if ($index === 'value') { + // user is coming from API. + $index = $parts[\count($parts) - 2]; + } + $index = (int)$index; + + // get actions from $this->data + $actions = []; + if (isset($this->data['rule-action']) && \is_array($this->data['rule-action'])) { + $actions = $this->data['rule-action']; + } + if (isset($this->data['rule-actions']) && \is_array($this->data['rule-actions'])) { + $actions = $this->data['rule-actions']; + } + + // loop all rule-actions. // check if rule-action-value matches the thing. - - if (\is_array($this->data['rule-action'])) { - $name = $this->data['rule-action'][$index] ?? 'invalid'; - $value = $this->data['rule-action-value'][$index] ?? false; + if (\is_array($actions)) { + $name = $this->getRuleActionName($index); + $value = $this->getRuleActionValue($index); switch ($name) { default: @@ -271,6 +288,8 @@ class FireflyValidator extends Validator } /** + * TODO This method uses a lot of if-then to handle the API calls as well. Fix. + * * @param $attribute * * @return bool @@ -280,20 +299,60 @@ class FireflyValidator extends Validator // get the index from a string like "rule-trigger-value.2". $parts = explode('.', $attribute); $index = $parts[\count($parts) - 1]; + // if the index is not a number, then we might be dealing with an API $attribute + // which is formatted "rule-triggers.0.value" + if ($index === 'value') { + $index = $parts[\count($parts) - 2]; + } + $index = (int)$index; + + // get triggers from $this->data + $triggers = []; + if (isset($this->data['rule-trigger']) && \is_array($this->data['rule-trigger'])) { + $triggers = $this->data['rule-trigger']; + } + if (isset($this->data['rule-triggers']) && \is_array($this->data['rule-triggers'])) { + $triggers = $this->data['rule-triggers']; + } // loop all rule-triggers. // check if rule-value matches the thing. - if (\is_array($this->data['rule-trigger'])) { + if (\is_array($triggers)) { $name = $this->getRuleTriggerName($index); $value = $this->getRuleTriggerValue($index); // break on some easy checks: switch ($name) { case 'amount_less': + case 'amount_more': + case 'amount_exactly': $result = is_numeric($value); if (false === $result) { return false; } + break; + case 'from_account_starts': + case 'from_account_ends': + case 'from_account_is': + case 'from_account_contains': + case 'to_account_starts': + case 'to_account_ends': + case 'to_account_is': + case 'to_account_contains': + case 'description_starts': + case 'description_ends': + case 'description_contains': + case 'description_is': + case 'category_is': + case 'budget_is': + case 'tag_is': + case 'currency_is': + case 'notes_contain': + case 'notes_start': + case 'notes_end': + case 'notes_are': + return \strlen($value) > 0; + break; case 'transaction_type': $count = TransactionType::where('type', $value)->count(); @@ -489,23 +548,71 @@ class FireflyValidator extends Validator } /** + * TODO this method needs a lot of logic to be able to handle API calls. Fix that. + * * @param int $index * * @return string */ - private function getRuleTriggerName($index): string + private function getRuleActionName(int $index): string { - return $this->data['rule-trigger'][$index] ?? 'invalid'; + $name = $this->data['rule-action'][$index] ?? 'invalid'; + if (!isset($this->data['rule-action'][$index])) { + $name = $this->data['rule-actions'][$index]['name'] ?? 'invalid'; + } + + return $name; } /** + * TODO this method needs a lot of logic to be able to handle API calls. Fix that. + * * @param int $index * * @return string */ - private function getRuleTriggerValue($index): string + private function getRuleActionValue(int $index): string { - return $this->data['rule-trigger-value'][$index] ?? ''; + $value = $this->data['rule-action-value'][$index] ?? ''; + if (!isset($this->data['rule-action-value'][$index])) { + $value = $this->data['rule-actions'][$index]['value'] ?? ''; + } + + return $value; + } + + /** + * TODO this method needs a lot of logic to be able to handle API calls. Fix that. + * + * @param int $index + * + * @return string + */ + private function getRuleTriggerName(int $index): string + { + $name = $this->data['rule-trigger'][$index] ?? 'invalid'; + if (!isset($this->data['rule-trigger'][$index])) { + $name = $this->data['rule-triggers'][$index]['name'] ?? 'invalid'; + } + + return $name; + } + + /** + * TODO this method needs a lot of logic to be able to handle API calls. Fix that. + * + * @param int $index + * + * @return string + */ + private function getRuleTriggerValue(int $index): string + { + $value = $this->data['rule-trigger-value'][$index] ?? ''; + if (!isset($this->data['rule-trigger-value'][$index])) { + $value = $this->data['rule-triggers'][$index]['value'] ?? ''; + } + + return $value; } /** diff --git a/changelog.md b/changelog.md index d16497a54c..6358b2b40b 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.7.5] - 2018-07-02 +### Added +- A new feature called "recurring transactions" that will make Firefly III automatically create transactions for you. +- New API end points for attachments, available budgets, budgets, budget limits, categories, configuration, currency exchange rates, journal links, link types, piggy banks, preferences, recurring transactions, rules, rule groups and tags. +- Added support for YunoHost. + +### Changed +- The 2FA secret is visible so you can type it into 2FA apps. +- Bunq and Spectre imports will now ask to apply rules. +- Sandstorm users can now make API keys. + +### Fixed +- Various typo's in the English translations. [issue 1493](https://github.com/firefly-iii/firefly-iii/issues/1493) +- Bug where Spectre was never called [issue 1492](https://github.com/firefly-iii/firefly-iii/issues/1492) +- Clear cache after journal is created through API [issue 1483](https://github.com/firefly-iii/firefly-iii/issues/1483) +- Make sure docker directories exist [issue 1500](https://github.com/firefly-iii/firefly-iii/issues/1500) +- Broken link to bill edit [issue 1505](https://github.com/firefly-iii/firefly-iii/issues/1505) +- Several bugs in the editing of split transactions [issue 1509](https://github.com/firefly-iii/firefly-iii/issues/1509) +- Import routine ignored formatting of several date fields [issue 1510](https://github.com/firefly-iii/firefly-iii/issues/1510) +- Piggy bank events now show the correct currency [issue 1446](https://github.com/firefly-iii/firefly-iii/issues/1446) +- Inactive accounts are no longer suggested [issue 1463](https://github.com/firefly-iii/firefly-iii/issues/1463) +- Some income / expense charts are less confusing [issue 1518](https://github.com/firefly-iii/firefly-iii/issues/1518) +- Validation bug in multi-currency create view [issue 1521](https://github.com/firefly-iii/firefly-iii/issues/1521) +- Bug where imported transfers would be stored incorrectly. + ## [4.7.4] - 2015-06-03 ### Added - [Issue 1409](https://github.com/firefly-iii/firefly-iii/issues/1409), add Indian Rupee and explain that users can do this themselves [issue 1413](https://github.com/firefly-iii/firefly-iii/issues/1413) diff --git a/composer.json b/composer.json index 9abb0009b2..66b1ca985a 100644 --- a/composer.json +++ b/composer.json @@ -1,128 +1,129 @@ { - "name": "grumpydictator/firefly-iii", - "description": "Firefly III: a personal finances manager.", - "keywords": [ - "finance", - "finances", - "manager", - "management", - "euro", - "dollar", - "laravel", - "money", - "currency", - "financials", - "financial", - "budgets", - "administration", - "tool", - "tooling", - "help", - "helper", - "assistant", - "planning", - "organizing", - "bills", - "personal finance", - "budgets", - "budgeting", - "budgeting tool", - "budgeting application", - "transactions", - "self hosted", - "self-hosted", - "transfers", - "management" - ], - "license": "GPL-3.0-or-later", - "homepage": "https://github.com/firefly-iii/firefly-iii", - "type": "project", - "authors": [{ - "name": "James Cole", - "email": "thegrumpydictator@gmail.com", - "homepage": "https://github.com/firefly-iii", - "role": "Developer" - }], - "require": { - "php": ">=7.1.0", - "ext-bcmath": "*", - "ext-curl": "*", - "ext-gd": "*", - "ext-intl": "*", - "ext-xml": "*", - "ext-zip": "*", - "bacon/bacon-qr-code": "1.*", - "bunq/sdk_php": "dev-master#8c1faefc111d9b970168a1837ca165d854954941", - "davejamesmiller/laravel-breadcrumbs": "5.*", - "doctrine/dbal": "2.*", - "fideloper/proxy": "4.*", - "laravel/framework": "5.6.*", - "laravel/passport": "^5.0", - "laravelcollective/html": "5.6.*", - "league/commonmark": "0.*", - "league/csv": "9.*", - "league/fractal": "^0.17.0", - "pragmarx/google2fa": "3.*", - "pragmarx/google2fa-laravel": "0.*", - "rcrowe/twigbridge": "0.9.*", - "rmccue/requests": "1.*", - "twig/twig": "1.*" - }, - "require-dev": { - "barryvdh/laravel-ide-helper": "2.*", - "filp/whoops": "2.*", - "fzaninotto/faker": "1.*", - "johnkary/phpunit-speedtrap": "^3.0", - "mockery/mockery": "^1.0", - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "~7.0", - "roave/security-advisories": "dev-master" - }, - "autoload": { - "classmap": [ - "database/seeds", - "database/factories" - ], - "psr-4": { - "FireflyIII\\": "app/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "extra": { - "laravel": { - "dont-discover": [] - } - }, - "scripts": { - "pre-install-cmd": [ - "@php -r \"if (!(getenv('DYNO'))===false){file_exists('.env') || copy('.env.heroku', '.env');}\"" - ], - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "post-create-project-cmd": [ - "@php artisan key:generate" - ], - "post-autoload-dump": [ - "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump" - ], - "post-update-cmd": [ - "php artisan firefly:upgrade-database", - "php artisan firefly:verify", - "php artisan firefly:instructions update", - "php artisan passport:install" - ], - "post-install-cmd": [ - "php artisan firefly:instructions install" - ] - }, - "config": { - "preferred-install": "dist", - "sort-packages": true, - "optimize-autoloader": true - } + "name": "grumpydictator/firefly-iii", + "description": "Firefly III: a personal finances manager.", + "keywords": [ + "finance", + "finances", + "manager", + "management", + "euro", + "dollar", + "laravel", + "money", + "currency", + "financials", + "financial", + "budgets", + "administration", + "tool", + "tooling", + "help", + "helper", + "assistant", + "planning", + "organizing", + "bills", + "personal finance", + "budgets", + "budgeting", + "budgeting tool", + "budgeting application", + "transactions", + "self hosted", + "self-hosted", + "transfers", + "management" + ], + "license": "GPL-3.0-or-later", + "homepage": "https://github.com/firefly-iii/firefly-iii", + "type": "project", + "authors": [ + { + "name": "James Cole", + "email": "thegrumpydictator@gmail.com", + "homepage": "https://github.com/firefly-iii", + "role": "Developer" + } + ], + "require": { + "php": ">=7.1.0", + "ext-bcmath": "*", + "ext-curl": "*", + "ext-gd": "*", + "ext-intl": "*", + "ext-xml": "*", + "ext-zip": "*", + "bacon/bacon-qr-code": "1.*", + "bunq/sdk_php": "dev-master#8c1faefc111d9b970168a1837ca165d854954941", + "davejamesmiller/laravel-breadcrumbs": "5.*", + "doctrine/dbal": "2.*", + "fideloper/proxy": "4.*", + "laravel/framework": "5.6.*", + "laravel/passport": "^5.0", + "laravelcollective/html": "5.6.*", + "league/commonmark": "0.*", + "league/csv": "9.*", + "league/fractal": "^0.17.0", + "pragmarx/google2fa": "3.*", + "pragmarx/google2fa-laravel": "0.*", + "rcrowe/twigbridge": "0.9.*", + "twig/twig": "1.*" + }, + "require-dev": { + "barryvdh/laravel-ide-helper": "2.*", + "filp/whoops": "2.*", + "fzaninotto/faker": "1.*", + "johnkary/phpunit-speedtrap": "^3.0", + "mockery/mockery": "^1.0", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "~7.0", + "roave/security-advisories": "dev-master" + }, + "autoload": { + "classmap": [ + "database/seeds", + "database/factories" + ], + "psr-4": { + "FireflyIII\\": "app/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "scripts": { + "pre-install-cmd": [ + "@php -r \"if (!(getenv('DYNO'))===false){file_exists('.env') || copy('.env.heroku', '.env');}\"" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate" + ], + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump" + ], + "post-update-cmd": [ + "php artisan firefly:upgrade-database", + "php artisan firefly:verify", + "php artisan firefly:instructions update", + "php artisan passport:install" + ], + "post-install-cmd": [ + "php artisan firefly:instructions install" + ] + }, + "config": { + "preferred-install": "dist", + "sort-packages": true, + "optimize-autoloader": true + } } diff --git a/composer.lock b/composer.lock index e1f8241676..a37ccfe587 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "312e75271d4570f83bc7554892a3b6ab", + "content-hash": "dcaf20ad3436c4fc4cbebeee09c9de1f", "packages": [ { "name": "bacon/bacon-qr-code", @@ -717,16 +717,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v2.1.0", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5" + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/3f00985deec8df53d4cc1e5c33619bda1ee309a5", - "reference": "3f00985deec8df53d4cc1e5c33619bda1ee309a5", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/92a2c3768d50e21a1f26a53cb795ce72806266c5", + "reference": "92a2c3768d50e21a1f26a53cb795ce72806266c5", "shasum": "" }, "require": { @@ -762,7 +762,7 @@ "cron", "schedule" ], - "time": "2018-04-06T15:51:55+00:00" + "time": "2018-06-06T03:12:17+00:00" }, { "name": "egulias/email-validator", @@ -1150,16 +1150,16 @@ }, { "name": "laravel/framework", - "version": "v5.6.23", + "version": "v5.6.26", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f547f0a71a12763d1adb8493237d541c9e3a5d10" + "reference": "7047df295e77cecb6a2f84736a732af66cc6789c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f547f0a71a12763d1adb8493237d541c9e3a5d10", - "reference": "f547f0a71a12763d1adb8493237d541c9e3a5d10", + "url": "https://api.github.com/repos/laravel/framework/zipball/7047df295e77cecb6a2f84736a732af66cc6789c", + "reference": "7047df295e77cecb6a2f84736a732af66cc6789c", "shasum": "" }, "require": { @@ -1285,7 +1285,7 @@ "framework", "laravel" ], - "time": "2018-05-22T14:55:57+00:00" + "time": "2018-06-20T14:21:11+00:00" }, { "name": "laravel/passport", @@ -1358,16 +1358,16 @@ }, { "name": "laravelcollective/html", - "version": "v5.6.9", + "version": "v5.6.10", "source": { "type": "git", "url": "https://github.com/LaravelCollective/html.git", - "reference": "fda9d3dad85ecea609ef9c6323d6923536cf5643" + "reference": "974605fcd22a7e4d19f0b2ef635a0d1d7400387d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LaravelCollective/html/zipball/fda9d3dad85ecea609ef9c6323d6923536cf5643", - "reference": "fda9d3dad85ecea609ef9c6323d6923536cf5643", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/974605fcd22a7e4d19f0b2ef635a0d1d7400387d", + "reference": "974605fcd22a7e4d19f0b2ef635a0d1d7400387d", "shasum": "" }, "require": { @@ -1422,7 +1422,7 @@ ], "description": "HTML and Form Builders for the Laravel Framework", "homepage": "https://laravelcollective.com", - "time": "2018-05-30T16:09:07+00:00" + "time": "2018-06-18T15:04:16+00:00" }, { "name": "lcobucci/jwt", @@ -2079,16 +2079,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.12", + "version": "v2.0.15", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" + "reference": "10bcb46e8f3d365170f6de9d05245aa066b81f09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", - "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/10bcb46e8f3d365170f6de9d05245aa066b81f09", + "reference": "10bcb46e8f3d365170f6de9d05245aa066b81f09", "shasum": "" }, "require": { @@ -2120,10 +2120,11 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2018-04-04T21:24:14+00:00" + "time": "2018-06-08T15:26:40+00:00" }, { "name": "phpseclib/phpseclib", @@ -2695,67 +2696,18 @@ ], "time": "2018-02-08T15:59:23+00:00" }, - { - "name": "rmccue/requests", - "version": "v1.7.0", - "source": { - "type": "git", - "url": "https://github.com/rmccue/Requests.git", - "reference": "87932f52ffad70504d93f04f15690cf16a089546" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", - "reference": "87932f52ffad70504d93f04f15690cf16a089546", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "requests/test-server": "dev-master" - }, - "type": "library", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/rmccue/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ], - "time": "2016-10-13T00:11:37+00:00" - }, { "name": "swiftmailer/swiftmailer", - "version": "v6.0.2", + "version": "v6.1.0", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc" + "reference": "0ff595e1d9d7d1c929b2a5f7b774bbcfbbd81587" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/412333372fb6c8ffb65496a2bbd7321af75733fc", - "reference": "412333372fb6c8ffb65496a2bbd7321af75733fc", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0ff595e1d9d7d1c929b2a5f7b774bbcfbbd81587", + "reference": "0ff595e1d9d7d1c929b2a5f7b774bbcfbbd81587", "shasum": "" }, "require": { @@ -2766,10 +2718,14 @@ "mockery/mockery": "~0.9.1", "symfony/phpunit-bridge": "~3.3@dev" }, + "suggest": { + "ext-intl": "Needed to support internationalized email addresses", + "true/punycode": "Needed to support internationalized email addresses, if ext-intl is not installed" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.0-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -2791,26 +2747,26 @@ } ], "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "http://swiftmailer.symfony.com", + "homepage": "https://swiftmailer.symfony.com", "keywords": [ "email", "mail", "mailer" ], - "time": "2017-09-30T22:39:41+00:00" + "time": "2018-07-02T20:24:38+00:00" }, { "name": "symfony/console", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2d5d973bf9933d46802b01010bd25c800c87c242" + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2d5d973bf9933d46802b01010bd25c800c87c242", - "reference": "2d5d973bf9933d46802b01010bd25c800c87c242", + "url": "https://api.github.com/repos/symfony/console/zipball/70591cda56b4b47c55776ac78e157c4bb6c8b43f", + "reference": "70591cda56b4b47c55776ac78e157c4bb6c8b43f", "shasum": "" }, "require": { @@ -2865,11 +2821,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2922,16 +2878,16 @@ }, { "name": "symfony/debug", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b" + "reference": "dbe0fad88046a755dcf9379f2964c61a02f5ae3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/449f8b00b28ab6e6912c3e6b920406143b27193b", - "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b", + "url": "https://api.github.com/repos/symfony/debug/zipball/dbe0fad88046a755dcf9379f2964c61a02f5ae3d", + "reference": "dbe0fad88046a755dcf9379f2964c61a02f5ae3d", "shasum": "" }, "require": { @@ -2974,11 +2930,11 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-05-16T14:33:22+00:00" + "time": "2018-06-08T09:39:36+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -3041,16 +2997,16 @@ }, { "name": "symfony/finder", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "087e2ee0d74464a4c6baac4e90417db7477dc238" + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/087e2ee0d74464a4c6baac4e90417db7477dc238", - "reference": "087e2ee0d74464a4c6baac4e90417db7477dc238", + "url": "https://api.github.com/repos/symfony/finder/zipball/84714b8417d19e4ba02ea78a41a975b3efaafddb", + "reference": "84714b8417d19e4ba02ea78a41a975b3efaafddb", "shasum": "" }, "require": { @@ -3086,20 +3042,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-05-16T14:33:22+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "a916c88390fb861ee21f12a92b107d51bb68af99" + "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/a916c88390fb861ee21f12a92b107d51bb68af99", - "reference": "a916c88390fb861ee21f12a92b107d51bb68af99", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4f9c7cf962e635b0b26b14500ac046e07dbef7f3", + "reference": "4f9c7cf962e635b0b26b14500ac046e07dbef7f3", "shasum": "" }, "require": { @@ -3140,20 +3096,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-05-25T14:55:38+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/http-kernel", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90" + "reference": "29c094a1c4f8209b7e033f612cbbd69029e38955" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90", - "reference": "b5ab9d4cdbfd369083744b6b5dfbf454e31e5f90", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/29c094a1c4f8209b7e033f612cbbd69029e38955", + "reference": "29c094a1c4f8209b7e033f612cbbd69029e38955", "shasum": "" }, "require": { @@ -3161,13 +3117,13 @@ "psr/log": "~1.0", "symfony/debug": "~3.4|~4.0", "symfony/event-dispatcher": "~4.1", - "symfony/http-foundation": "~4.1", + "symfony/http-foundation": "^4.1.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/config": "<3.4", "symfony/dependency-injection": "<4.1", - "symfony/var-dumper": "<4.1", + "symfony/var-dumper": "<4.1.1", "twig/twig": "<1.34|<2.4,>=2" }, "provide": { @@ -3188,7 +3144,7 @@ "symfony/stopwatch": "~3.4|~4.0", "symfony/templating": "~3.4|~4.0", "symfony/translation": "~3.4|~4.0", - "symfony/var-dumper": "~4.1" + "symfony/var-dumper": "^4.1.1" }, "suggest": { "symfony/browser-kit": "", @@ -3227,7 +3183,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2018-05-30T12:52:34+00:00" + "time": "2018-06-25T13:06:45+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3508,16 +3464,16 @@ }, { "name": "symfony/process", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "73445bd33b0d337c060eef9652b94df72b6b3434" + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/73445bd33b0d337c060eef9652b94df72b6b3434", - "reference": "73445bd33b0d337c060eef9652b94df72b6b3434", + "url": "https://api.github.com/repos/symfony/process/zipball/1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", + "reference": "1d1677391ecf00d1c5b9482d6050c0c27aa3ac3a", "shasum": "" }, "require": { @@ -3553,7 +3509,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-05-31T10:17:53+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -3617,16 +3573,16 @@ }, { "name": "symfony/routing", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "180b51c66d10f09e562c9ebc395b39aacb2cf8a2" + "reference": "b38b9797327b26ea2e4146a40e6e2dc9820a6932" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/180b51c66d10f09e562c9ebc395b39aacb2cf8a2", - "reference": "180b51c66d10f09e562c9ebc395b39aacb2cf8a2", + "url": "https://api.github.com/repos/symfony/routing/zipball/b38b9797327b26ea2e4146a40e6e2dc9820a6932", + "reference": "b38b9797327b26ea2e4146a40e6e2dc9820a6932", "shasum": "" }, "require": { @@ -3639,7 +3595,6 @@ }, "require-dev": { "doctrine/annotations": "~1.0", - "doctrine/common": "~2.2", "psr/log": "~1.0", "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", @@ -3691,20 +3646,20 @@ "uri", "url" ], - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-06-19T21:38:16+00:00" }, { "name": "symfony/translation", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "16328f5b217cebc8dd4adfe4aeeaa8c377581f5a" + "reference": "b6d8164085ee0b6debcd1b7a131fd6f63bb04854" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/16328f5b217cebc8dd4adfe4aeeaa8c377581f5a", - "reference": "16328f5b217cebc8dd4adfe4aeeaa8c377581f5a", + "url": "https://api.github.com/repos/symfony/translation/zipball/b6d8164085ee0b6debcd1b7a131fd6f63bb04854", + "reference": "b6d8164085ee0b6debcd1b7a131fd6f63bb04854", "shasum": "" }, "require": { @@ -3760,20 +3715,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2018-05-30T07:26:09+00:00" + "time": "2018-06-22T08:59:39+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "bc88ad53e825ebacc7b190bbd360781fce381c64" + "reference": "b2eebaec085d1f2cafbad7644733d494a3bbbc9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/bc88ad53e825ebacc7b190bbd360781fce381c64", - "reference": "bc88ad53e825ebacc7b190bbd360781fce381c64", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b2eebaec085d1f2cafbad7644733d494a3bbbc9b", + "reference": "b2eebaec085d1f2cafbad7644733d494a3bbbc9b", "shasum": "" }, "require": { @@ -3835,7 +3790,7 @@ "debug", "dump" ], - "time": "2018-04-29T07:56:09+00:00" + "time": "2018-06-23T12:23:56+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -3951,28 +3906,28 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c" + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", - "reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", + "reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8.35 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" + "dev-master": "2.5-dev" } }, "autoload": { @@ -3982,7 +3937,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause-Attribution" + "BSD-3-Clause" ], "authors": [ { @@ -3997,20 +3952,20 @@ "env", "environment" ], - "time": "2016-09-01T10:05:43+00:00" + "time": "2018-07-01T10:25:50+00:00" }, { "name": "zendframework/zend-diactoros", - "version": "1.7.2", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "741e7a571836f038de731105f4742ca8a164e43a" + "reference": "11c9c1835e60eef6f9234377a480fcec096ebd9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/741e7a571836f038de731105f4742ca8a164e43a", - "reference": "741e7a571836f038de731105f4742ca8a164e43a", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/11c9c1835e60eef6f9234377a480fcec096ebd9e", + "reference": "11c9c1835e60eef6f9234377a480fcec096ebd9e", "shasum": "" }, "require": { @@ -4029,12 +3984,22 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev", - "dev-develop": "1.8.x-dev", + "dev-master": "1.8.x-dev", + "dev-develop": "1.9.x-dev", "dev-release-2.0": "2.0.x-dev" } }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], "psr-4": { "Zend\\Diactoros\\": "src/" } @@ -4050,7 +4015,7 @@ "psr", "psr-7" ], - "time": "2018-05-29T16:53:08+00:00" + "time": "2018-06-27T18:52:43+00:00" } ], "packages-dev": [ @@ -4232,16 +4197,16 @@ }, { "name": "filp/whoops", - "version": "2.1.14", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "c6081b8838686aa04f1e83ba7e91f78b7b2a23e6" + "reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/c6081b8838686aa04f1e83ba7e91f78b7b2a23e6", - "reference": "c6081b8838686aa04f1e83ba7e91f78b7b2a23e6", + "url": "https://api.github.com/repos/filp/whoops/zipball/181c4502d8f34db7aed7bfe88d4f87875b8e947a", + "reference": "181c4502d8f34db7aed7bfe88d4f87875b8e947a", "shasum": "" }, "require": { @@ -4249,9 +4214,9 @@ "psr/log": "^1.0.1" }, "require-dev": { - "mockery/mockery": "0.9.*", + "mockery/mockery": "^0.9 || ^1.0", "phpunit/phpunit": "^4.8.35 || ^5.7", - "symfony/var-dumper": "^2.6 || ^3.0" + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -4260,7 +4225,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -4289,7 +4254,7 @@ "throwable", "whoops" ], - "time": "2017-11-23T18:22:44+00:00" + "time": "2018-03-03T17:56:25+00:00" }, { "name": "fzaninotto/faker", @@ -4505,16 +4470,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/478465659fd987669df0bd8a9bf22a8710e5f1b6", - "reference": "478465659fd987669df0bd8a9bf22a8710e5f1b6", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { @@ -4549,7 +4514,7 @@ "object", "object graph" ], - "time": "2018-05-29T17:25:09+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", @@ -5016,16 +4981,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc" + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/e20525b0c2945c7c317fff95660698cb3d2a53bc", - "reference": "e20525b0c2945c7c317fff95660698cb3d2a53bc", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cecbc684605bb0cc288828eb5d65d93d5c676d3c", + "reference": "cecbc684605bb0cc288828eb5d65d93d5c676d3c", "shasum": "" }, "require": { @@ -5059,7 +5024,7 @@ "filesystem", "iterator" ], - "time": "2018-05-28T12:13:49+00:00" + "time": "2018-06-11T11:44:00+00:00" }, { "name": "phpunit/php-text-template", @@ -5202,16 +5167,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.2.2", + "version": "7.2.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3cf0836680bf5c365c627e8566d46c9e1f544db9" + "reference": "400a3836ee549ae6f665323ac3f21e27eac7155f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3cf0836680bf5c365c627e8566d46c9e1f544db9", - "reference": "3cf0836680bf5c365c627e8566d46c9e1f544db9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/400a3836ee549ae6f665323ac3f21e27eac7155f", + "reference": "400a3836ee549ae6f665323ac3f21e27eac7155f", "shasum": "" }, "require": { @@ -5227,7 +5192,7 @@ "php": "^7.1", "phpspec/prophecy": "^1.7", "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.0", "sebastian/comparator": "^3.0", @@ -5282,7 +5247,7 @@ "testing", "xunit" ], - "time": "2018-06-01T07:54:27+00:00" + "time": "2018-06-21T13:13:39+00:00" }, { "name": "roave/security-advisories", @@ -5290,12 +5255,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4d93302eb93402083f5abe72002fe8dc35e12dae" + "reference": "5c802c6300dca269edde06c6ae8c7eb561de3176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4d93302eb93402083f5abe72002fe8dc35e12dae", - "reference": "4d93302eb93402083f5abe72002fe8dc35e12dae", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/5c802c6300dca269edde06c6ae8c7eb561de3176", + "reference": "5c802c6300dca269edde06c6ae8c7eb561de3176", "shasum": "" }, "conflict": { @@ -5346,7 +5311,7 @@ "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/magento2ce": ">=2,<2.2", + "magento/magento2ce": ">=2,<2.3", "monolog/monolog": ">=1.8,<1.12", "namshi/jose": "<2.2", "onelogin/php-saml": "<2.10.4", @@ -5363,6 +5328,7 @@ "propel/propel1": ">=1,<=1.7.1", "pusher/pusher-php-server": "<2.2.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "sensiolabs/connect": "<4.2.3", "shopware/shopware": "<5.3.7", "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", @@ -5385,7 +5351,7 @@ "symfony/routing": ">=2,<2.0.19", "symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", @@ -5396,7 +5362,7 @@ "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1,<2.1.2|>=2.1.0-beta1,<2.1.3", + "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", "titon/framework": ">=0,<9.9.99", "twig/twig": "<1.20", "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", @@ -5448,7 +5414,7 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-05-30T02:58:56+00:00" + "time": "2018-07-03T10:42:36+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -5497,16 +5463,16 @@ }, { "name": "sebastian/comparator", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5" + "reference": "591a30922f54656695e59b1f39501aec513403da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ed5fd2281113729f1ebcc64d101ad66028aeb3d5", - "reference": "ed5fd2281113729f1ebcc64d101ad66028aeb3d5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/591a30922f54656695e59b1f39501aec513403da", + "reference": "591a30922f54656695e59b1f39501aec513403da", "shasum": "" }, "require": { @@ -5557,20 +5523,20 @@ "compare", "equality" ], - "time": "2018-04-18T13:33:00+00:00" + "time": "2018-06-14T15:05:28+00:00" }, { "name": "sebastian/diff", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/e09160918c66281713f1c324c1f4c4c3037ba1e8", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { @@ -5613,7 +5579,7 @@ "unidiff", "unified diff" ], - "time": "2018-02-01T13:45:15+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", @@ -6015,7 +5981,7 @@ }, { "name": "symfony/class-loader", - "version": "v3.4.11", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", @@ -6071,16 +6037,16 @@ }, { "name": "symfony/config", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "5ceefc256caecc3e25147c4e5b933de71d0020c4" + "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/5ceefc256caecc3e25147c4e5b933de71d0020c4", - "reference": "5ceefc256caecc3e25147c4e5b933de71d0020c4", + "url": "https://api.github.com/repos/symfony/config/zipball/e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", + "reference": "e57e7b573df9d0eaa8c0152768c708ee7ea2b8e5", "shasum": "" }, "require": { @@ -6130,11 +6096,11 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-05-16T14:33:22+00:00" + "time": "2018-06-20T11:15:17+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -6184,7 +6150,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -6233,7 +6199,7 @@ }, { "name": "symfony/yaml", - "version": "v4.1.0", + "version": "v4.1.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", diff --git a/config/app.php b/config/app.php index 953ed046ee..5127530c6d 100644 --- a/config/app.php +++ b/config/app.php @@ -98,6 +98,7 @@ return [ FireflyIII\Providers\SearchServiceProvider::class, FireflyIII\Providers\TagServiceProvider::class, FireflyIII\Providers\AdminServiceProvider::class, + FireflyIII\Providers\RecurringServiceProvider::class, ], diff --git a/config/firefly.php b/config/firefly.php index c0fe9ed9ea..3ce05abf00 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -83,16 +83,16 @@ use FireflyIII\TransactionRules\Triggers\UserAction; */ return [ - 'configuration' => [ + 'configuration' => [ 'single_user_mode' => true, 'is_demo_site' => false, ], - 'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true, - 'version' => '4.7.4', - 'api_version' => '0.3', - 'db_version' => 4, - 'maxUploadSize' => 15242880, - 'allowedMimes' => [ + 'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true, + 'version' => '4.7.5', + 'api_version' => '0.4', + 'db_version' => 4, + 'maxUploadSize' => 15242880, + 'allowedMimes' => [ /* plain files */ 'text/plain', @@ -154,8 +154,8 @@ return [ 'application/vnd.oasis.opendocument.database', 'application/vnd.oasis.opendocument.image', ], - 'list_length' => 10, - 'export_formats' => [ + 'list_length' => 10, + 'export_formats' => [ 'csv' => CsvExporter::class, ], 'default_export_format' => 'csv', @@ -260,6 +260,7 @@ return [ // models 'account' => \FireflyIII\Models\Account::class, 'attachment' => \FireflyIII\Models\Attachment::class, + 'availableBudget' => \FireflyIII\Models\AvailableBudget::class, 'bill' => \FireflyIII\Models\Bill::class, 'budget' => \FireflyIII\Models\Budget::class, 'budgetLimit' => \FireflyIII\Models\BudgetLimit::class, @@ -269,8 +270,10 @@ return [ 'journalLink' => \FireflyIII\Models\TransactionJournalLink::class, 'currency' => \FireflyIII\Models\TransactionCurrency::class, 'piggyBank' => \FireflyIII\Models\PiggyBank::class, + 'preference' => \FireflyIII\Models\Preference::class, 'tj' => \FireflyIII\Models\TransactionJournal::class, 'tag' => \FireflyIII\Models\Tag::class, + 'recurrence' => \FireflyIII\Models\Recurrence::class, 'rule' => \FireflyIII\Models\Rule::class, 'ruleGroup' => \FireflyIII\Models\RuleGroup::class, 'exportJob' => \FireflyIII\Models\ExportJob::class, @@ -279,7 +282,7 @@ return [ 'user' => \FireflyIII\User::class, // strings - 'import_provider' => \FireflyIII\Support\Binder\ImportProvider::class, + 'import_provider' => \FireflyIII\Support\Binder\ImportProvider::class, // dates 'start_date' => \FireflyIII\Support\Binder\Date::class, diff --git a/config/services.php b/config/services.php index 01c5a3d90e..d72eea32f3 100644 --- a/config/services.php +++ b/config/services.php @@ -51,10 +51,12 @@ return [ 'secret' => env('SPARKPOST_SECRET'), ], - 'stripe' => [ + 'stripe' => [ 'model' => FireflyIII\User::class, 'key' => env('STRIPE_KEY'), 'secret' => env('STRIPE_SECRET'), ], - + 'mandrill' => [ + 'secret' => env('MANDRILL_SECRET'), + ], ]; diff --git a/config/twigbridge.php b/config/twigbridge.php index f4a2c2167a..f6a6e22ae3 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -188,7 +188,8 @@ return [ 'is_safe' => [ 'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location', 'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall', 'password', 'nonSelectableBalance', 'nonSelectableAmount', - 'number', 'assetAccountList','amountNoCurrency','currencyList','ruleGroupList','assetAccountCheckList','ruleGroupListWithEmpty' + 'number', 'assetAccountList','amountNoCurrency','currencyList','ruleGroupList','assetAccountCheckList','ruleGroupListWithEmpty', + 'piggyBankList','currencyListEmpty','activeAssetAccountList' ], ], 'Form' => [ diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index 04a90e6a2a..9a702f7221 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -46,6 +46,24 @@ $factory->define( } ); + +$factory->define( + FireflyIII\Models\Attachment::class, + function (Faker\Generator $faker) { + return [ + 'user_id' => 1, + 'attachable_id' => 1, + 'attachable_type' => \FireflyIII\Models\TransactionJournal::class, + 'md5' => md5($faker->words(6, true)), + 'mime' => 'text/plain', + 'size' => 1, + 'filename' => 'ok', + 'uploaded' => true, + + ]; + } +); + $factory->define( FireflyIII\Models\CurrencyExchangeRate::class, function (Faker\Generator $faker) { @@ -245,13 +263,13 @@ $factory->define( 'transaction_amount' => (string)$faker->randomFloat(2, -100, 100), 'destination_amount' => (string)$faker->randomFloat(2, -100, 100), 'opposing_account_id' => $faker->numberBetween(1, 10), - 'source_account_id' => $faker->numberBetween(1, 10), + 'source_id' => $faker->numberBetween(1, 10), 'opposing_account_name' => $faker->words(3, true), 'description' => $faker->words(3, true), - 'source_account_name' => $faker->words(3, true), - 'destination_account_id' => $faker->numberBetween(1, 10), + 'source_name' => $faker->words(3, true), + 'destination_id' => $faker->numberBetween(1, 10), 'date' => new Carbon, - 'destination_account_name' => $faker->words(3, true), + 'destination_name' => $faker->words(3, true), 'amount' => (string)$faker->randomFloat(2, -100, 100), 'budget_id' => 0, 'category' => $faker->words(3, true), diff --git a/database/migrations/2018_04_29_174524_changes_for_v474.php b/database/migrations/2018_04_29_174524_changes_for_v474.php index cdc6208f35..c9c79e6e82 100644 --- a/database/migrations/2018_04_29_174524_changes_for_v474.php +++ b/database/migrations/2018_04_29_174524_changes_for_v474.php @@ -37,7 +37,6 @@ class ChangesForV474 extends Migration */ public function down() { - // } /** diff --git a/database/migrations/2018_06_08_200526_changes_for_v475.php b/database/migrations/2018_06_08_200526_changes_for_v475.php new file mode 100644 index 0000000000..fa85ed5456 --- /dev/null +++ b/database/migrations/2018_06_08_200526_changes_for_v475.php @@ -0,0 +1,131 @@ +increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('transaction_type_id', false, true); + + $table->string('title', 1024); + $table->text('description'); + + $table->date('first_date'); + $table->date('repeat_until')->nullable(); + $table->date('latest_date')->nullable(); + $table->smallInteger('repetitions', false, true); + + $table->boolean('apply_rules')->default(true); + $table->boolean('active')->default(true); + + // also separate: + // category, budget, tags, notes, bill, piggy bank + + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); + } + ); + + Schema::create( + 'recurrences_transactions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); + $table->integer('transaction_currency_id', false, true); + $table->integer('foreign_currency_id', false, true)->nullable(); + $table->integer('source_id', false, true); + $table->integer('destination_id', false, true); + + $table->decimal('amount', 22, 12); + $table->decimal('foreign_amount', 22, 12)->nullable(); + $table->string('description', 1024); + + + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); + $table->foreign('foreign_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); + $table->foreign('source_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('destination_id')->references('id')->on('accounts')->onDelete('cascade'); + } + ); + + + Schema::create( + 'recurrences_repetitions', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); + $table->string('repetition_type', 50); + $table->string('repetition_moment', 50); + $table->smallInteger('repetition_skip', false, true); + $table->smallInteger('weekend', false, true); + + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + } + ); + + Schema::create( + 'recurrences_meta', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); + + $table->string('name', 50); + $table->text('value'); + + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + } + ); + + Schema::create( + 'rt_meta', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('rt_id', false, true); + + $table->string('name', 50); + $table->text('value'); + + $table->foreign('rt_id')->references('id')->on('recurrences_transactions')->onDelete('cascade'); + } + ); + + + } +} diff --git a/phpunit.coverage.specific.xml b/phpunit.coverage.specific.xml index da49035fc3..9448630a43 100644 --- a/phpunit.coverage.specific.xml +++ b/phpunit.coverage.specific.xml @@ -30,12 +30,9 @@ app/Http/breadcrumbs.php - - vendor/ - - + diff --git a/phpunit.coverage.xml b/phpunit.coverage.xml index cfca4f71ee..7a3119d118 100644 --- a/phpunit.coverage.xml +++ b/phpunit.coverage.xml @@ -30,12 +30,9 @@ app/Http/breadcrumbs.php - - vendor/ - - + diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 67e4805c53..275f3fb144 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -26,12 +26,12 @@ var allCharts = {}; */ var colourSet = [ [53, 124, 165], - [0, 141, 76], + [0, 141, 76], // green [219, 139, 11], - [202, 25, 90], + [202, 25, 90], // paars rood-ish #CA195A [85, 82, 153], [66, 133, 244], - [219, 68, 55], + [219, 68, 55], // red #DB4437 [244, 180, 0], [15, 157, 88], [171, 71, 188], @@ -205,6 +205,21 @@ function columnChart(URI, container) { } +/** + * + * @param URI + * @param container + */ +function columnChartCustomColours(URI, container) { + "use strict"; + var colorData = false; + var options = $.extend(true, {}, defaultChartOptions); + var chartType = 'bar'; + + drawAChart(URI, container, chartType, options, colorData); + +} + /** * * @param URI diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index 10ecacbcc3..ba48ecd4e6 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -83,6 +83,7 @@ $(function () { function currencySelect(e) { "use strict"; + console.log('In currencySelect() because somebody clicked a .currency-option.'); // clicked on var target = $(e.target); // target is the tag. @@ -105,6 +106,7 @@ function currencySelect(e) { var id = target.data('id'); // update the hidden input: + console.log('Updated ' + hiddenInputName + ' to ID ' + id); $('input[name="' + hiddenInputName + '"]').val(id); // update the symbol: diff --git a/public/js/ff/recurring/create.js b/public/js/ff/recurring/create.js new file mode 100644 index 0000000000..4d47190f70 --- /dev/null +++ b/public/js/ff/recurring/create.js @@ -0,0 +1,246 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * + * This file is part of Firefly III. + * + * Firefly III is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Firefly III is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Firefly III. If not, see . + */ + +/** global: Modernizr, currencies */ + +var calendar; + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } + initializeButtons(); + initializeAutoComplete(); + respondToFirstDateChange(); + respondToRepetitionEnd(); + $('.switch-button').on('click', switchTransactionType); + $('#ffInput_repetition_end').on('change', respondToRepetitionEnd); + $('#ffInput_first_date').on('change', respondToFirstDateChange); + + // create calendar on load: + calendar = $('#recurring_calendar').fullCalendar( + { + defaultDate: '2018-06-13', + editable: false, + height: 400, + width: 200, + contentHeight: 400, + aspectRatio: 1.25, + eventLimit: true, + eventSources: [], + }); + + $('#calendar-link').on('click', showRepCalendar); +}); + +/** + * + */ +function showRepCalendar() { + + // pre-append URL with repetition info: + var newEventsUri = eventsUri + '?type=' + $('#ffInput_repetition_type').val(); + newEventsUri += '&skip=' + $('#ffInput_skip').val(); + newEventsUri += '&ends=' + $('#ffInput_repetition_end').val(); + newEventsUri += '&end_date=' + $('#ffInput_repeat_until').val(); + newEventsUri += '&reps=' + $('#ffInput_repetitions').val(); + newEventsUri += '&first_date=' + $('#ffInput_first_date').val(); + newEventsUri += '&weekend=' + $('#ffInput_weekend').val(); + + // remove all event sources from calendar: + calendar.fullCalendar('removeEventSources'); + + // add a new one: + calendar.fullCalendar('addEventSource', newEventsUri); + $('#calendarModal').modal('show'); + + return false; +} + +function respondToRepetitionEnd() { + var obj = $('#ffInput_repetition_end'); + var value = obj.val(); + switch (value) { + case 'forever': + $('#repeat_until_holder').hide(); + $('#repetitions_holder').hide(); + break; + case 'until_date': + $('#repeat_until_holder').show(); + $('#repetitions_holder').hide(); + break; + case 'times': + $('#repeat_until_holder').hide(); + $('#repetitions_holder').show(); + break; + } + + +} + +function respondToFirstDateChange() { + var obj = $('#ffInput_first_date'); + var select = $('#ffInput_repetition_type'); + var date = obj.val(); + select.prop('disabled', true); + + // preselected value: + var preSelected = oldRepetitionType; + if(preSelected === '') { + preSelected = select.val(); + } + + $.getJSON(suggestUri, {date: date,pre_select: preSelected}).fail(function () { + console.error('Could not load repetition suggestions'); + alert('Could not load repetition suggestions'); + }).done(parseRepetitionSuggestions); +} + +function parseRepetitionSuggestions(data) { + + var select = $('#ffInput_repetition_type'); + select.empty(); + var opt; + for (var k in data) { + if (data.hasOwnProperty(k)) { + console.log('label: ' + data[k].label + ', selected: ' + data[k].selected); + opt = $('' + middleCrumbName[what] + ''); - $('#transaction-btn').text(button[what]); + $('.transaction-btn').text(button[what]); } /** @@ -150,22 +158,23 @@ function updateLayout() { */ function updateForm() { "use strict"; + console.log('Now in updateForm()'); $('input[name="what"]').val(what); - var destName = $('#ffInput_destination_account_name'); - var srcName = $('#ffInput_source_account_name'); + var destName = $('#ffInput_destination_name'); + var srcName = $('#ffInput_source_name'); switch (what) { case 'withdrawal': // show source_id and dest_name - document.getElementById('source_account_id_holder').style.display = 'block'; - document.getElementById('destination_account_name_holder').style.display = 'block'; + document.getElementById('source_id_holder').style.display = 'block'; + document.getElementById('destination_name_holder').style.display = 'block'; // hide others: - document.getElementById('source_account_name_holder').style.display = 'none'; - document.getElementById('destination_account_id_holder').style.display = 'none'; + document.getElementById('source_name_holder').style.display = 'none'; + document.getElementById('destination_id_holder').style.display = 'none'; document.getElementById('budget_id_holder').style.display = 'block'; // hide piggy bank: @@ -185,12 +194,12 @@ function updateForm() { break; case 'deposit': // show source_name and dest_id: - document.getElementById('source_account_name_holder').style.display = 'block'; - document.getElementById('destination_account_id_holder').style.display = 'block'; + document.getElementById('source_name_holder').style.display = 'block'; + document.getElementById('destination_id_holder').style.display = 'block'; // hide others: - document.getElementById('source_account_id_holder').style.display = 'none'; - document.getElementById('destination_account_name_holder').style.display = 'none'; + document.getElementById('source_id_holder').style.display = 'none'; + document.getElementById('destination_name_holder').style.display = 'none'; // hide budget document.getElementById('budget_id_holder').style.display = 'none'; @@ -212,19 +221,19 @@ function updateForm() { break; case 'transfer': // show source_id and dest_id: - document.getElementById('source_account_id_holder').style.display = 'block'; - document.getElementById('destination_account_id_holder').style.display = 'block'; + document.getElementById('source_id_holder').style.display = 'block'; + document.getElementById('destination_id_holder').style.display = 'block'; // hide others: - document.getElementById('source_account_name_holder').style.display = 'none'; - document.getElementById('destination_account_name_holder').style.display = 'none'; + document.getElementById('source_name_holder').style.display = 'none'; + document.getElementById('destination_name_holder').style.display = 'none'; // hide budget document.getElementById('budget_id_holder').style.display = 'none'; // optional piggies var showPiggies = 'block'; - if (piggiesLength === 0) { + if ($('#ffInput_piggy_bank_id option').length === 0) { showPiggies = 'none'; } document.getElementById('piggy_bank_id_holder').style.display = showPiggies; @@ -233,7 +242,6 @@ function updateForm() { break; } // get instructions all the time. - //updateNativeCurrency(useAccountCurrency); console.log('End of update form'); selectsDifferentSource(); selectsDifferentDestination(); @@ -288,10 +296,10 @@ function clickButton(e) { */ function getAccountId() { if (what === "withdrawal") { - return $('select[name="source_account_id"]').val(); + return $('select[name="source_id"]').val(); } if (what === "deposit" || what === "transfer") { - return $('select[name="destination_account_id"]').val(); + return $('select[name="destination_id"]').val(); } return undefined; } diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js index cf5248e021..30ce29ea8f 100644 --- a/public/js/ff/transactions/single/edit.js +++ b/public/js/ff/transactions/single/edit.js @@ -38,12 +38,12 @@ $(document).ready(function () { $('#ffInput_amount').on('change', convertForeignToNative); // respond to transfer changes: - $('#ffInput_source_account_id').on('change', function () { + $('#ffInput_source_id').on('change', function () { validateCurrencyForTransfer(); // update the two source account currency ID fields (initial value): initCurrencyIdValues(); }); - $('#ffInput_destination_account_id').on('change', function () { + $('#ffInput_destination_id').on('change', function () { validateCurrencyForTransfer(); // update the two source account currency ID fields (initial value): initCurrencyIdValues(); @@ -77,9 +77,9 @@ function initCurrencyIdValues() { $('input[name="destination_account_currency"]').val(currencyId); return; } - var sourceAccount = $('select[name="source_account_id"]').val(); + var sourceAccount = $('select[name="source_id"]').val(); console.log('Source account is ' + sourceAccount); - var destAccount = $('select[name="destination_account_id"]').val(); + var destAccount = $('select[name="destination_id"]').val(); console.log('Destination account is ' + destAccount); var sourceCurrency = parseInt(accountInfo[sourceAccount].preferredCurrency); @@ -134,10 +134,10 @@ function updateInitialPage() { function getAccountId() { console.log('in getAccountId()'); if (journal.transaction_type.type === "Withdrawal") { - return $('select[name="source_account_id"]').val(); + return $('select[name="source_id"]').val(); } if (journal.transaction_type.type === "Deposit") { - return $('select[name="destination_account_id"]').val(); + return $('select[name="destination_id"]').val(); } alert('Cannot handle ' + journal.transaction_type.type); diff --git a/public/js/ff/transactions/split/edit.js b/public/js/ff/transactions/split/edit.js index a99a39f651..f3178e0f4e 100644 --- a/public/js/ff/transactions/split/edit.js +++ b/public/js/ff/transactions/split/edit.js @@ -100,6 +100,12 @@ function removeDivRow(e) { } var row = $(e.target); var index = row.data('split'); + if (typeof index === 'undefined') { + var parent = row.parent(); + index = parent.data('split'); + console.log('Parent. ' + parent.className); + } + console.log('Split index is "' + index + '"'); $('div.split_row[data-split="' + index + '"]').remove(); @@ -185,15 +191,15 @@ function resetDivSplits() { var input = $(v); input.attr('name', 'transactions[' + i + '][transaction_description]'); }); - // ends with ][destination_account_name] + // ends with ][destination_name] $.each($('input[name$="][destination_name]"]'), function (i, v) { var input = $(v); - input.attr('name', 'transactions[' + i + '][destination_account_name]'); + input.attr('name', 'transactions[' + i + '][destination_name]'); }); - // ends with ][source_account_name] + // ends with ][source_name] $.each($('input[name$="][source_name]"]'), function (i, v) { var input = $(v); - input.attr('name', 'transactions[' + i + '][source_account_name]'); + input.attr('name', 'transactions[' + i + '][source_name]'); }); // ends with ][amount] $.each($('input[name$="][amount]"]'), function (i, v) { diff --git a/public/lib/fc/fullcalendar.css b/public/lib/fc/fullcalendar.css new file mode 100644 index 0000000000..dcbc999758 --- /dev/null +++ b/public/lib/fc/fullcalendar.css @@ -0,0 +1,1293 @@ +/*! + * FullCalendar v3.9.0 + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */ +.fc { + direction: ltr; + text-align: left; } + +.fc-rtl { + text-align: right; } + +body .fc { + /* extra precedence to overcome jqui */ + font-size: 1em; } + +/* Colors +--------------------------------------------------------------------------------------------------*/ +.fc-highlight { + /* when user is selecting cells */ + background: #bce8f1; + opacity: .3; } + +.fc-bgevent { + /* default look for background events */ + background: #8fdf82; + opacity: .3; } + +.fc-nonbusiness { + /* default look for non-business-hours areas */ + /* will inherit .fc-bgevent's styles */ + background: #d7d7d7; } + +/* Buttons (styled ') + .click(function (ev) { + // don't process clicks for disabled buttons + if (!buttonEl.hasClass(theme.getClass('stateDisabled'))) { + buttonClick(ev); + // after the click action, if the button becomes the "active" tab, or disabled, + // it should never have a hover class, so remove it now. + if (buttonEl.hasClass(theme.getClass('stateActive')) || + buttonEl.hasClass(theme.getClass('stateDisabled'))) { + buttonEl.removeClass(theme.getClass('stateHover')); + } + } + }) + .mousedown(function () { + // the *down* effect (mouse pressed in). + // only on buttons that are not the "active" tab, or disabled + buttonEl + .not('.' + theme.getClass('stateActive')) + .not('.' + theme.getClass('stateDisabled')) + .addClass(theme.getClass('stateDown')); + }) + .mouseup(function () { + // undo the *down* effect + buttonEl.removeClass(theme.getClass('stateDown')); + }) + .hover(function () { + // the *hover* effect. + // only on buttons that are not the "active" tab, or disabled + buttonEl + .not('.' + theme.getClass('stateActive')) + .not('.' + theme.getClass('stateDisabled')) + .addClass(theme.getClass('stateHover')); + }, function () { + // undo the *hover* effect + buttonEl + .removeClass(theme.getClass('stateHover')) + .removeClass(theme.getClass('stateDown')); // if mouseleave happens before mouseup + }); + groupChildren = groupChildren.add(buttonEl); + } + } + }); + if (isOnlyButtons) { + groupChildren + .first().addClass(theme.getClass('cornerLeft')).end() + .last().addClass(theme.getClass('cornerRight')).end(); + } + if (groupChildren.length > 1) { + groupEl = $('
'); + if (isOnlyButtons) { + groupEl.addClass(theme.getClass('buttonGroup')); + } + groupEl.append(groupChildren); + sectionEl.append(groupEl); + } + else { + sectionEl.append(groupChildren); // 1 or 0 children + } + }); + } + return sectionEl; + }; + Toolbar.prototype.updateTitle = function (text) { + if (this.el) { + this.el.find('h2').text(text); + } + }; + Toolbar.prototype.activateButton = function (buttonName) { + if (this.el) { + this.el.find('.fc-' + buttonName + '-button') + .addClass(this.calendar.theme.getClass('stateActive')); + } + }; + Toolbar.prototype.deactivateButton = function (buttonName) { + if (this.el) { + this.el.find('.fc-' + buttonName + '-button') + .removeClass(this.calendar.theme.getClass('stateActive')); + } + }; + Toolbar.prototype.disableButton = function (buttonName) { + if (this.el) { + this.el.find('.fc-' + buttonName + '-button') + .prop('disabled', true) + .addClass(this.calendar.theme.getClass('stateDisabled')); + } + }; + Toolbar.prototype.enableButton = function (buttonName) { + if (this.el) { + this.el.find('.fc-' + buttonName + '-button') + .prop('disabled', false) + .removeClass(this.calendar.theme.getClass('stateDisabled')); + } + }; + Toolbar.prototype.getViewsWithButtons = function () { + return this.viewsWithButtons; + }; + return Toolbar; +}()); +exports.default = Toolbar; + + +/***/ }), +/* 240 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var options_1 = __webpack_require__(32); +var locale_1 = __webpack_require__(31); +var Model_1 = __webpack_require__(48); +var OptionsManager = /** @class */ (function (_super) { + tslib_1.__extends(OptionsManager, _super); + function OptionsManager(_calendar, overrides) { + var _this = _super.call(this) || this; + _this._calendar = _calendar; + _this.overrides = $.extend({}, overrides); // make a copy + _this.dynamicOverrides = {}; + _this.compute(); + return _this; + } + OptionsManager.prototype.add = function (newOptionHash) { + var optionCnt = 0; + var optionName; + this.recordOverrides(newOptionHash); // will trigger this model's watchers + for (optionName in newOptionHash) { + optionCnt++; + } + // special-case handling of single option change. + // if only one option change, `optionName` will be its name. + if (optionCnt === 1) { + if (optionName === 'height' || optionName === 'contentHeight' || optionName === 'aspectRatio') { + this._calendar.updateViewSize(true); // isResize=true + return; + } + else if (optionName === 'defaultDate') { + return; // can't change date this way. use gotoDate instead + } + else if (optionName === 'businessHours') { + return; // this model already reacts to this + } + else if (/^(event|select)(Overlap|Constraint|Allow)$/.test(optionName)) { + return; // doesn't affect rendering. only interactions. + } + else if (optionName === 'timezone') { + this._calendar.view.flash('initialEvents'); + return; + } + } + // catch-all. rerender the header and footer and rebuild/rerender the current view + this._calendar.renderHeader(); + this._calendar.renderFooter(); + // even non-current views will be affected by this option change. do before rerender + // TODO: detangle + this._calendar.viewsByType = {}; + this._calendar.reinitView(); + }; + // Computes the flattened options hash for the calendar and assigns to `this.options`. + // Assumes this.overrides and this.dynamicOverrides have already been initialized. + OptionsManager.prototype.compute = function () { + var locale; + var localeDefaults; + var isRTL; + var dirDefaults; + var rawOptions; + locale = util_1.firstDefined(// explicit locale option given? + this.dynamicOverrides.locale, this.overrides.locale); + localeDefaults = locale_1.localeOptionHash[locale]; + if (!localeDefaults) { + locale = options_1.globalDefaults.locale; + localeDefaults = locale_1.localeOptionHash[locale] || {}; + } + isRTL = util_1.firstDefined(// based on options computed so far, is direction RTL? + this.dynamicOverrides.isRTL, this.overrides.isRTL, localeDefaults.isRTL, options_1.globalDefaults.isRTL); + dirDefaults = isRTL ? options_1.rtlDefaults : {}; + this.dirDefaults = dirDefaults; + this.localeDefaults = localeDefaults; + rawOptions = options_1.mergeOptions([ + options_1.globalDefaults, + dirDefaults, + localeDefaults, + this.overrides, + this.dynamicOverrides + ]); + locale_1.populateInstanceComputableOptions(rawOptions); // fill in gaps with computed options + this.reset(rawOptions); + }; + // stores the new options internally, but does not rerender anything. + OptionsManager.prototype.recordOverrides = function (newOptionHash) { + var optionName; + for (optionName in newOptionHash) { + this.dynamicOverrides[optionName] = newOptionHash[optionName]; + } + this._calendar.viewSpecManager.clearCache(); // the dynamic override invalidates the options in this cache, so just clear it + this.compute(); // this.options needs to be recomputed after the dynamic override + }; + return OptionsManager; +}(Model_1.default)); +exports.default = OptionsManager; + + +/***/ }), +/* 241 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var moment = __webpack_require__(0); +var $ = __webpack_require__(3); +var ViewRegistry_1 = __webpack_require__(22); +var util_1 = __webpack_require__(4); +var options_1 = __webpack_require__(32); +var locale_1 = __webpack_require__(31); +var ViewSpecManager = /** @class */ (function () { + function ViewSpecManager(optionsManager, _calendar) { + this.optionsManager = optionsManager; + this._calendar = _calendar; + this.clearCache(); + } + ViewSpecManager.prototype.clearCache = function () { + this.viewSpecCache = {}; + }; + // Gets information about how to create a view. Will use a cache. + ViewSpecManager.prototype.getViewSpec = function (viewType) { + var cache = this.viewSpecCache; + return cache[viewType] || (cache[viewType] = this.buildViewSpec(viewType)); + }; + // Given a duration singular unit, like "week" or "day", finds a matching view spec. + // Preference is given to views that have corresponding buttons. + ViewSpecManager.prototype.getUnitViewSpec = function (unit) { + var viewTypes; + var i; + var spec; + if ($.inArray(unit, util_1.unitsDesc) !== -1) { + // put views that have buttons first. there will be duplicates, but oh well + viewTypes = this._calendar.header.getViewsWithButtons(); // TODO: include footer as well? + $.each(ViewRegistry_1.viewHash, function (viewType) { + viewTypes.push(viewType); + }); + for (i = 0; i < viewTypes.length; i++) { + spec = this.getViewSpec(viewTypes[i]); + if (spec) { + if (spec.singleUnit === unit) { + return spec; + } + } + } + } + }; + // Builds an object with information on how to create a given view + ViewSpecManager.prototype.buildViewSpec = function (requestedViewType) { + var viewOverrides = this.optionsManager.overrides.views || {}; + var specChain = []; // for the view. lowest to highest priority + var defaultsChain = []; // for the view. lowest to highest priority + var overridesChain = []; // for the view. lowest to highest priority + var viewType = requestedViewType; + var spec; // for the view + var overrides; // for the view + var durationInput; + var duration; + var unit; + // iterate from the specific view definition to a more general one until we hit an actual View class + while (viewType) { + spec = ViewRegistry_1.viewHash[viewType]; + overrides = viewOverrides[viewType]; + viewType = null; // clear. might repopulate for another iteration + if (typeof spec === 'function') { + spec = { 'class': spec }; + } + if (spec) { + specChain.unshift(spec); + defaultsChain.unshift(spec.defaults || {}); + durationInput = durationInput || spec.duration; + viewType = viewType || spec.type; + } + if (overrides) { + overridesChain.unshift(overrides); // view-specific option hashes have options at zero-level + durationInput = durationInput || overrides.duration; + viewType = viewType || overrides.type; + } + } + spec = util_1.mergeProps(specChain); + spec.type = requestedViewType; + if (!spec['class']) { + return false; + } + // fall back to top-level `duration` option + durationInput = durationInput || + this.optionsManager.dynamicOverrides.duration || + this.optionsManager.overrides.duration; + if (durationInput) { + duration = moment.duration(durationInput); + if (duration.valueOf()) { + unit = util_1.computeDurationGreatestUnit(duration, durationInput); + spec.duration = duration; + spec.durationUnit = unit; + // view is a single-unit duration, like "week" or "day" + // incorporate options for this. lowest priority + if (duration.as(unit) === 1) { + spec.singleUnit = unit; + overridesChain.unshift(viewOverrides[unit] || {}); + } + } + } + spec.defaults = options_1.mergeOptions(defaultsChain); + spec.overrides = options_1.mergeOptions(overridesChain); + this.buildViewSpecOptions(spec); + this.buildViewSpecButtonText(spec, requestedViewType); + return spec; + }; + // Builds and assigns a view spec's options object from its already-assigned defaults and overrides + ViewSpecManager.prototype.buildViewSpecOptions = function (spec) { + var optionsManager = this.optionsManager; + spec.options = options_1.mergeOptions([ + options_1.globalDefaults, + spec.defaults, + optionsManager.dirDefaults, + optionsManager.localeDefaults, + optionsManager.overrides, + spec.overrides, + optionsManager.dynamicOverrides // dynamically set via setter. highest precedence + ]); + locale_1.populateInstanceComputableOptions(spec.options); + }; + // Computes and assigns a view spec's buttonText-related options + ViewSpecManager.prototype.buildViewSpecButtonText = function (spec, requestedViewType) { + var optionsManager = this.optionsManager; + // given an options object with a possible `buttonText` hash, lookup the buttonText for the + // requested view, falling back to a generic unit entry like "week" or "day" + function queryButtonText(options) { + var buttonText = options.buttonText || {}; + return buttonText[requestedViewType] || + // view can decide to look up a certain key + (spec.buttonTextKey ? buttonText[spec.buttonTextKey] : null) || + // a key like "month" + (spec.singleUnit ? buttonText[spec.singleUnit] : null); + } + // highest to lowest priority + spec.buttonTextOverride = + queryButtonText(optionsManager.dynamicOverrides) || + queryButtonText(optionsManager.overrides) || // constructor-specified buttonText lookup hash takes precedence + spec.overrides.buttonText; // `buttonText` for view-specific options is a string + // highest to lowest priority. mirrors buildViewSpecOptions + spec.buttonTextDefault = + queryButtonText(optionsManager.localeDefaults) || + queryButtonText(optionsManager.dirDefaults) || + spec.defaults.buttonText || // a single string. from ViewSubclass.defaults + queryButtonText(options_1.globalDefaults) || + (spec.duration ? this._calendar.humanizeDuration(spec.duration) : null) || // like "3 days" + requestedViewType; // fall back to given view name + }; + return ViewSpecManager; +}()); +exports.default = ViewSpecManager; + + +/***/ }), +/* 242 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var EventPeriod_1 = __webpack_require__(243); +var ArrayEventSource_1 = __webpack_require__(52); +var EventSource_1 = __webpack_require__(6); +var EventSourceParser_1 = __webpack_require__(38); +var SingleEventDef_1 = __webpack_require__(13); +var EventInstanceGroup_1 = __webpack_require__(18); +var EmitterMixin_1 = __webpack_require__(11); +var ListenerMixin_1 = __webpack_require__(7); +var EventManager = /** @class */ (function () { + function EventManager(calendar) { + this.calendar = calendar; + this.stickySource = new ArrayEventSource_1.default(calendar); + this.otherSources = []; + } + EventManager.prototype.requestEvents = function (start, end, timezone, force) { + if (force || + !this.currentPeriod || + !this.currentPeriod.isWithinRange(start, end) || + timezone !== this.currentPeriod.timezone) { + this.setPeriod(// will change this.currentPeriod + new EventPeriod_1.default(start, end, timezone)); + } + return this.currentPeriod.whenReleased(); + }; + // Source Adding/Removing + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.addSource = function (eventSource) { + this.otherSources.push(eventSource); + if (this.currentPeriod) { + this.currentPeriod.requestSource(eventSource); // might release + } + }; + EventManager.prototype.removeSource = function (doomedSource) { + util_1.removeExact(this.otherSources, doomedSource); + if (this.currentPeriod) { + this.currentPeriod.purgeSource(doomedSource); // might release + } + }; + EventManager.prototype.removeAllSources = function () { + this.otherSources = []; + if (this.currentPeriod) { + this.currentPeriod.purgeAllSources(); // might release + } + }; + // Source Refetching + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.refetchSource = function (eventSource) { + var currentPeriod = this.currentPeriod; + if (currentPeriod) { + currentPeriod.freeze(); + currentPeriod.purgeSource(eventSource); + currentPeriod.requestSource(eventSource); + currentPeriod.thaw(); + } + }; + EventManager.prototype.refetchAllSources = function () { + var currentPeriod = this.currentPeriod; + if (currentPeriod) { + currentPeriod.freeze(); + currentPeriod.purgeAllSources(); + currentPeriod.requestSources(this.getSources()); + currentPeriod.thaw(); + } + }; + // Source Querying + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.getSources = function () { + return [this.stickySource].concat(this.otherSources); + }; + // like querySources, but accepts multple match criteria (like multiple IDs) + EventManager.prototype.multiQuerySources = function (matchInputs) { + // coerce into an array + if (!matchInputs) { + matchInputs = []; + } + else if (!$.isArray(matchInputs)) { + matchInputs = [matchInputs]; + } + var matchingSources = []; + var i; + // resolve raw inputs to real event source objects + for (i = 0; i < matchInputs.length; i++) { + matchingSources.push.apply(// append + matchingSources, this.querySources(matchInputs[i])); + } + return matchingSources; + }; + // matchInput can either by a real event source object, an ID, or the function/URL for the source. + // returns an array of matching source objects. + EventManager.prototype.querySources = function (matchInput) { + var sources = this.otherSources; + var i; + var source; + // given a proper event source object + for (i = 0; i < sources.length; i++) { + source = sources[i]; + if (source === matchInput) { + return [source]; + } + } + // an ID match + source = this.getSourceById(EventSource_1.default.normalizeId(matchInput)); + if (source) { + return [source]; + } + // parse as an event source + matchInput = EventSourceParser_1.default.parse(matchInput, this.calendar); + if (matchInput) { + return $.grep(sources, function (source) { + return isSourcesEquivalent(matchInput, source); + }); + } + }; + /* + ID assumed to already be normalized + */ + EventManager.prototype.getSourceById = function (id) { + return $.grep(this.otherSources, function (source) { + return source.id && source.id === id; + })[0]; + }; + // Event-Period + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.setPeriod = function (eventPeriod) { + if (this.currentPeriod) { + this.unbindPeriod(this.currentPeriod); + this.currentPeriod = null; + } + this.currentPeriod = eventPeriod; + this.bindPeriod(eventPeriod); + eventPeriod.requestSources(this.getSources()); + }; + EventManager.prototype.bindPeriod = function (eventPeriod) { + this.listenTo(eventPeriod, 'release', function (eventsPayload) { + this.trigger('release', eventsPayload); + }); + }; + EventManager.prototype.unbindPeriod = function (eventPeriod) { + this.stopListeningTo(eventPeriod); + }; + // Event Getting/Adding/Removing + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.getEventDefByUid = function (uid) { + if (this.currentPeriod) { + return this.currentPeriod.getEventDefByUid(uid); + } + }; + EventManager.prototype.addEventDef = function (eventDef, isSticky) { + if (isSticky) { + this.stickySource.addEventDef(eventDef); + } + if (this.currentPeriod) { + this.currentPeriod.addEventDef(eventDef); // might release + } + }; + EventManager.prototype.removeEventDefsById = function (eventId) { + this.getSources().forEach(function (eventSource) { + eventSource.removeEventDefsById(eventId); + }); + if (this.currentPeriod) { + this.currentPeriod.removeEventDefsById(eventId); // might release + } + }; + EventManager.prototype.removeAllEventDefs = function () { + this.getSources().forEach(function (eventSource) { + eventSource.removeAllEventDefs(); + }); + if (this.currentPeriod) { + this.currentPeriod.removeAllEventDefs(); + } + }; + // Event Mutating + // ----------------------------------------------------------------------------------------------------------------- + /* + Returns an undo function. + */ + EventManager.prototype.mutateEventsWithId = function (eventDefId, eventDefMutation) { + var currentPeriod = this.currentPeriod; + var eventDefs; + var undoFuncs = []; + if (currentPeriod) { + currentPeriod.freeze(); + eventDefs = currentPeriod.getEventDefsById(eventDefId); + eventDefs.forEach(function (eventDef) { + // add/remove esp because id might change + currentPeriod.removeEventDef(eventDef); + undoFuncs.push(eventDefMutation.mutateSingle(eventDef)); + currentPeriod.addEventDef(eventDef); + }); + currentPeriod.thaw(); + return function () { + currentPeriod.freeze(); + for (var i = 0; i < eventDefs.length; i++) { + currentPeriod.removeEventDef(eventDefs[i]); + undoFuncs[i](); + currentPeriod.addEventDef(eventDefs[i]); + } + currentPeriod.thaw(); + }; + } + return function () { }; + }; + /* + copies and then mutates + */ + EventManager.prototype.buildMutatedEventInstanceGroup = function (eventDefId, eventDefMutation) { + var eventDefs = this.getEventDefsById(eventDefId); + var i; + var defCopy; + var allInstances = []; + for (i = 0; i < eventDefs.length; i++) { + defCopy = eventDefs[i].clone(); + if (defCopy instanceof SingleEventDef_1.default) { + eventDefMutation.mutateSingle(defCopy); + allInstances.push.apply(allInstances, // append + defCopy.buildInstances()); + } + } + return new EventInstanceGroup_1.default(allInstances); + }; + // Freezing + // ----------------------------------------------------------------------------------------------------------------- + EventManager.prototype.freeze = function () { + if (this.currentPeriod) { + this.currentPeriod.freeze(); + } + }; + EventManager.prototype.thaw = function () { + if (this.currentPeriod) { + this.currentPeriod.thaw(); + } + }; + // methods that simply forward to EventPeriod + EventManager.prototype.getEventDefsById = function (eventDefId) { + return this.currentPeriod.getEventDefsById(eventDefId); + }; + EventManager.prototype.getEventInstances = function () { + return this.currentPeriod.getEventInstances(); + }; + EventManager.prototype.getEventInstancesWithId = function (eventDefId) { + return this.currentPeriod.getEventInstancesWithId(eventDefId); + }; + EventManager.prototype.getEventInstancesWithoutId = function (eventDefId) { + return this.currentPeriod.getEventInstancesWithoutId(eventDefId); + }; + return EventManager; +}()); +exports.default = EventManager; +EmitterMixin_1.default.mixInto(EventManager); +ListenerMixin_1.default.mixInto(EventManager); +function isSourcesEquivalent(source0, source1) { + return source0.getPrimitive() === source1.getPrimitive(); +} + + +/***/ }), +/* 243 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var Promise_1 = __webpack_require__(20); +var EmitterMixin_1 = __webpack_require__(11); +var UnzonedRange_1 = __webpack_require__(5); +var EventInstanceGroup_1 = __webpack_require__(18); +var EventPeriod = /** @class */ (function () { + function EventPeriod(start, end, timezone) { + this.pendingCnt = 0; + this.freezeDepth = 0; + this.stuntedReleaseCnt = 0; + this.releaseCnt = 0; + this.start = start; + this.end = end; + this.timezone = timezone; + this.unzonedRange = new UnzonedRange_1.default(start.clone().stripZone(), end.clone().stripZone()); + this.requestsByUid = {}; + this.eventDefsByUid = {}; + this.eventDefsById = {}; + this.eventInstanceGroupsById = {}; + } + EventPeriod.prototype.isWithinRange = function (start, end) { + // TODO: use a range util function? + return !start.isBefore(this.start) && !end.isAfter(this.end); + }; + // Requesting and Purging + // ----------------------------------------------------------------------------------------------------------------- + EventPeriod.prototype.requestSources = function (sources) { + this.freeze(); + for (var i = 0; i < sources.length; i++) { + this.requestSource(sources[i]); + } + this.thaw(); + }; + EventPeriod.prototype.requestSource = function (source) { + var _this = this; + var request = { source: source, status: 'pending', eventDefs: null }; + this.requestsByUid[source.uid] = request; + this.pendingCnt += 1; + source.fetch(this.start, this.end, this.timezone).then(function (eventDefs) { + if (request.status !== 'cancelled') { + request.status = 'completed'; + request.eventDefs = eventDefs; + _this.addEventDefs(eventDefs); + _this.pendingCnt--; + _this.tryRelease(); + } + }, function () { + if (request.status !== 'cancelled') { + request.status = 'failed'; + _this.pendingCnt--; + _this.tryRelease(); + } + }); + }; + EventPeriod.prototype.purgeSource = function (source) { + var request = this.requestsByUid[source.uid]; + if (request) { + delete this.requestsByUid[source.uid]; + if (request.status === 'pending') { + request.status = 'cancelled'; + this.pendingCnt--; + this.tryRelease(); + } + else if (request.status === 'completed') { + request.eventDefs.forEach(this.removeEventDef.bind(this)); + } + } + }; + EventPeriod.prototype.purgeAllSources = function () { + var requestsByUid = this.requestsByUid; + var uid; + var request; + var completedCnt = 0; + for (uid in requestsByUid) { + request = requestsByUid[uid]; + if (request.status === 'pending') { + request.status = 'cancelled'; + } + else if (request.status === 'completed') { + completedCnt++; + } + } + this.requestsByUid = {}; + this.pendingCnt = 0; + if (completedCnt) { + this.removeAllEventDefs(); // might release + } + }; + // Event Definitions + // ----------------------------------------------------------------------------------------------------------------- + EventPeriod.prototype.getEventDefByUid = function (eventDefUid) { + return this.eventDefsByUid[eventDefUid]; + }; + EventPeriod.prototype.getEventDefsById = function (eventDefId) { + var a = this.eventDefsById[eventDefId]; + if (a) { + return a.slice(); // clone + } + return []; + }; + EventPeriod.prototype.addEventDefs = function (eventDefs) { + for (var i = 0; i < eventDefs.length; i++) { + this.addEventDef(eventDefs[i]); + } + }; + EventPeriod.prototype.addEventDef = function (eventDef) { + var eventDefsById = this.eventDefsById; + var eventDefId = eventDef.id; + var eventDefs = eventDefsById[eventDefId] || (eventDefsById[eventDefId] = []); + var eventInstances = eventDef.buildInstances(this.unzonedRange); + var i; + eventDefs.push(eventDef); + this.eventDefsByUid[eventDef.uid] = eventDef; + for (i = 0; i < eventInstances.length; i++) { + this.addEventInstance(eventInstances[i], eventDefId); + } + }; + EventPeriod.prototype.removeEventDefsById = function (eventDefId) { + var _this = this; + this.getEventDefsById(eventDefId).forEach(function (eventDef) { + _this.removeEventDef(eventDef); + }); + }; + EventPeriod.prototype.removeAllEventDefs = function () { + var isEmpty = $.isEmptyObject(this.eventDefsByUid); + this.eventDefsByUid = {}; + this.eventDefsById = {}; + this.eventInstanceGroupsById = {}; + if (!isEmpty) { + this.tryRelease(); + } + }; + EventPeriod.prototype.removeEventDef = function (eventDef) { + var eventDefsById = this.eventDefsById; + var eventDefs = eventDefsById[eventDef.id]; + delete this.eventDefsByUid[eventDef.uid]; + if (eventDefs) { + util_1.removeExact(eventDefs, eventDef); + if (!eventDefs.length) { + delete eventDefsById[eventDef.id]; + } + this.removeEventInstancesForDef(eventDef); + } + }; + // Event Instances + // ----------------------------------------------------------------------------------------------------------------- + EventPeriod.prototype.getEventInstances = function () { + var eventInstanceGroupsById = this.eventInstanceGroupsById; + var eventInstances = []; + var id; + for (id in eventInstanceGroupsById) { + eventInstances.push.apply(eventInstances, // append + eventInstanceGroupsById[id].eventInstances); + } + return eventInstances; + }; + EventPeriod.prototype.getEventInstancesWithId = function (eventDefId) { + var eventInstanceGroup = this.eventInstanceGroupsById[eventDefId]; + if (eventInstanceGroup) { + return eventInstanceGroup.eventInstances.slice(); // clone + } + return []; + }; + EventPeriod.prototype.getEventInstancesWithoutId = function (eventDefId) { + var eventInstanceGroupsById = this.eventInstanceGroupsById; + var matchingInstances = []; + var id; + for (id in eventInstanceGroupsById) { + if (id !== eventDefId) { + matchingInstances.push.apply(matchingInstances, // append + eventInstanceGroupsById[id].eventInstances); + } + } + return matchingInstances; + }; + EventPeriod.prototype.addEventInstance = function (eventInstance, eventDefId) { + var eventInstanceGroupsById = this.eventInstanceGroupsById; + var eventInstanceGroup = eventInstanceGroupsById[eventDefId] || + (eventInstanceGroupsById[eventDefId] = new EventInstanceGroup_1.default()); + eventInstanceGroup.eventInstances.push(eventInstance); + this.tryRelease(); + }; + EventPeriod.prototype.removeEventInstancesForDef = function (eventDef) { + var eventInstanceGroupsById = this.eventInstanceGroupsById; + var eventInstanceGroup = eventInstanceGroupsById[eventDef.id]; + var removeCnt; + if (eventInstanceGroup) { + removeCnt = util_1.removeMatching(eventInstanceGroup.eventInstances, function (currentEventInstance) { + return currentEventInstance.def === eventDef; + }); + if (!eventInstanceGroup.eventInstances.length) { + delete eventInstanceGroupsById[eventDef.id]; + } + if (removeCnt) { + this.tryRelease(); + } + } + }; + // Releasing and Freezing + // ----------------------------------------------------------------------------------------------------------------- + EventPeriod.prototype.tryRelease = function () { + if (!this.pendingCnt) { + if (!this.freezeDepth) { + this.release(); + } + else { + this.stuntedReleaseCnt++; + } + } + }; + EventPeriod.prototype.release = function () { + this.releaseCnt++; + this.trigger('release', this.eventInstanceGroupsById); + }; + EventPeriod.prototype.whenReleased = function () { + var _this = this; + if (this.releaseCnt) { + return Promise_1.default.resolve(this.eventInstanceGroupsById); + } + else { + return Promise_1.default.construct(function (onResolve) { + _this.one('release', onResolve); + }); + } + }; + EventPeriod.prototype.freeze = function () { + if (!(this.freezeDepth++)) { + this.stuntedReleaseCnt = 0; + } + }; + EventPeriod.prototype.thaw = function () { + if (!(--this.freezeDepth) && this.stuntedReleaseCnt && !this.pendingCnt) { + this.release(); + } + }; + return EventPeriod; +}()); +exports.default = EventPeriod; +EmitterMixin_1.default.mixInto(EventPeriod); + + +/***/ }), +/* 244 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var ListenerMixin_1 = __webpack_require__(7); +/* Creates a clone of an element and lets it track the mouse as it moves +----------------------------------------------------------------------------------------------------------------------*/ +var MouseFollower = /** @class */ (function () { + function MouseFollower(sourceEl, options) { + this.isFollowing = false; + this.isHidden = false; + this.isAnimating = false; // doing the revert animation? + this.options = options = options || {}; + this.sourceEl = sourceEl; + this.parentEl = options.parentEl ? $(options.parentEl) : sourceEl.parent(); // default to sourceEl's parent + } + // Causes the element to start following the mouse + MouseFollower.prototype.start = function (ev) { + if (!this.isFollowing) { + this.isFollowing = true; + this.y0 = util_1.getEvY(ev); + this.x0 = util_1.getEvX(ev); + this.topDelta = 0; + this.leftDelta = 0; + if (!this.isHidden) { + this.updatePosition(); + } + if (util_1.getEvIsTouch(ev)) { + this.listenTo($(document), 'touchmove', this.handleMove); + } + else { + this.listenTo($(document), 'mousemove', this.handleMove); + } + } + }; + // Causes the element to stop following the mouse. If shouldRevert is true, will animate back to original position. + // `callback` gets invoked when the animation is complete. If no animation, it is invoked immediately. + MouseFollower.prototype.stop = function (shouldRevert, callback) { + var _this = this; + var revertDuration = this.options.revertDuration; + var complete = function () { + _this.isAnimating = false; + _this.removeElement(); + _this.top0 = _this.left0 = null; // reset state for future updatePosition calls + if (callback) { + callback(); + } + }; + if (this.isFollowing && !this.isAnimating) { + this.isFollowing = false; + this.stopListeningTo($(document)); + if (shouldRevert && revertDuration && !this.isHidden) { + this.isAnimating = true; + this.el.animate({ + top: this.top0, + left: this.left0 + }, { + duration: revertDuration, + complete: complete + }); + } + else { + complete(); + } + } + }; + // Gets the tracking element. Create it if necessary + MouseFollower.prototype.getEl = function () { + var el = this.el; + if (!el) { + el = this.el = this.sourceEl.clone() + .addClass(this.options.additionalClass || '') + .css({ + position: 'absolute', + visibility: '', + display: this.isHidden ? 'none' : '', + margin: 0, + right: 'auto', + bottom: 'auto', + width: this.sourceEl.width(), + height: this.sourceEl.height(), + opacity: this.options.opacity || '', + zIndex: this.options.zIndex + }); + // we don't want long taps or any mouse interaction causing selection/menus. + // would use preventSelection(), but that prevents selectstart, causing problems. + el.addClass('fc-unselectable'); + el.appendTo(this.parentEl); + } + return el; + }; + // Removes the tracking element if it has already been created + MouseFollower.prototype.removeElement = function () { + if (this.el) { + this.el.remove(); + this.el = null; + } + }; + // Update the CSS position of the tracking element + MouseFollower.prototype.updatePosition = function () { + var sourceOffset; + var origin; + this.getEl(); // ensure this.el + // make sure origin info was computed + if (this.top0 == null) { + sourceOffset = this.sourceEl.offset(); + origin = this.el.offsetParent().offset(); + this.top0 = sourceOffset.top - origin.top; + this.left0 = sourceOffset.left - origin.left; + } + this.el.css({ + top: this.top0 + this.topDelta, + left: this.left0 + this.leftDelta + }); + }; + // Gets called when the user moves the mouse + MouseFollower.prototype.handleMove = function (ev) { + this.topDelta = util_1.getEvY(ev) - this.y0; + this.leftDelta = util_1.getEvX(ev) - this.x0; + if (!this.isHidden) { + this.updatePosition(); + } + }; + // Temporarily makes the tracking element invisible. Can be called before following starts + MouseFollower.prototype.hide = function () { + if (!this.isHidden) { + this.isHidden = true; + if (this.el) { + this.el.hide(); + } + } + }; + // Show the tracking element after it has been temporarily hidden + MouseFollower.prototype.show = function () { + if (this.isHidden) { + this.isHidden = false; + this.updatePosition(); + this.getEl().show(); + } + }; + return MouseFollower; +}()); +exports.default = MouseFollower; +ListenerMixin_1.default.mixInto(MouseFollower); + + +/***/ }), +/* 245 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var HitDragListener_1 = __webpack_require__(23); +var Interaction_1 = __webpack_require__(15); +var DateClicking = /** @class */ (function (_super) { + tslib_1.__extends(DateClicking, _super); + /* + component must implement: + - bindDateHandlerToEl + - getSafeHitFootprint + - getHitEl + */ + function DateClicking(component) { + var _this = _super.call(this, component) || this; + _this.dragListener = _this.buildDragListener(); + return _this; + } + DateClicking.prototype.end = function () { + this.dragListener.endInteraction(); + }; + DateClicking.prototype.bindToEl = function (el) { + var component = this.component; + var dragListener = this.dragListener; + component.bindDateHandlerToEl(el, 'mousedown', function (ev) { + if (!component.shouldIgnoreMouse()) { + dragListener.startInteraction(ev); + } + }); + component.bindDateHandlerToEl(el, 'touchstart', function (ev) { + if (!component.shouldIgnoreTouch()) { + dragListener.startInteraction(ev); + } + }); + }; + // Creates a listener that tracks the user's drag across day elements, for day clicking. + DateClicking.prototype.buildDragListener = function () { + var _this = this; + var component = this.component; + var dayClickHit; // null if invalid dayClick + var dragListener = new HitDragListener_1.default(component, { + scroll: this.opt('dragScroll'), + interactionStart: function () { + dayClickHit = dragListener.origHit; + }, + hitOver: function (hit, isOrig, origHit) { + // if user dragged to another cell at any point, it can no longer be a dayClick + if (!isOrig) { + dayClickHit = null; + } + }, + hitOut: function () { + dayClickHit = null; + }, + interactionEnd: function (ev, isCancelled) { + var componentFootprint; + if (!isCancelled && dayClickHit) { + componentFootprint = component.getSafeHitFootprint(dayClickHit); + if (componentFootprint) { + _this.view.triggerDayClick(componentFootprint, component.getHitEl(dayClickHit), ev); + } + } + } + }); + // because dragListener won't be called with any time delay, "dragging" will begin immediately, + // which will kill any touchmoving/scrolling. Prevent this. + dragListener.shouldCancelTouchScroll = false; + dragListener.scrollAlwaysKills = true; + return dragListener; + }; + return DateClicking; +}(Interaction_1.default)); +exports.default = DateClicking; + + +/***/ }), +/* 246 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var util_1 = __webpack_require__(4); +var EventRenderer_1 = __webpack_require__(42); +/* +Only handles foreground segs. +Does not own rendering. Use for low-level util methods by TimeGrid. +*/ +var TimeGridEventRenderer = /** @class */ (function (_super) { + tslib_1.__extends(TimeGridEventRenderer, _super); + function TimeGridEventRenderer(timeGrid, fillRenderer) { + var _this = _super.call(this, timeGrid, fillRenderer) || this; + _this.timeGrid = timeGrid; + return _this; + } + TimeGridEventRenderer.prototype.renderFgSegs = function (segs) { + this.renderFgSegsIntoContainers(segs, this.timeGrid.fgContainerEls); + }; + // Given an array of foreground segments, render a DOM element for each, computes position, + // and attaches to the column inner-container elements. + TimeGridEventRenderer.prototype.renderFgSegsIntoContainers = function (segs, containerEls) { + var segsByCol; + var col; + segsByCol = this.timeGrid.groupSegsByCol(segs); + for (col = 0; col < this.timeGrid.colCnt; col++) { + this.updateFgSegCoords(segsByCol[col]); + } + this.timeGrid.attachSegsByCol(segsByCol, containerEls); + }; + TimeGridEventRenderer.prototype.unrenderFgSegs = function () { + if (this.fgSegs) { + this.fgSegs.forEach(function (seg) { + seg.el.remove(); + }); + } + }; + // Computes a default event time formatting string if `timeFormat` is not explicitly defined + TimeGridEventRenderer.prototype.computeEventTimeFormat = function () { + return this.opt('noMeridiemTimeFormat'); // like "6:30" (no AM/PM) + }; + // Computes a default `displayEventEnd` value if one is not expliclty defined + TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () { + return true; + }; + // Renders the HTML for a single event segment's default rendering + TimeGridEventRenderer.prototype.fgSegHtml = function (seg, disableResizing) { + var view = this.view; + var calendar = view.calendar; + var componentFootprint = seg.footprint.componentFootprint; + var isAllDay = componentFootprint.isAllDay; + var eventDef = seg.footprint.eventDef; + var isDraggable = view.isEventDefDraggable(eventDef); + var isResizableFromStart = !disableResizing && seg.isStart && view.isEventDefResizableFromStart(eventDef); + var isResizableFromEnd = !disableResizing && seg.isEnd && view.isEventDefResizableFromEnd(eventDef); + var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd); + var skinCss = util_1.cssToStr(this.getSkinCss(eventDef)); + var timeText; + var fullTimeText; // more verbose time text. for the print stylesheet + var startTimeText; // just the start time text + classes.unshift('fc-time-grid-event', 'fc-v-event'); + // if the event appears to span more than one day... + if (view.isMultiDayRange(componentFootprint.unzonedRange)) { + // Don't display time text on segments that run entirely through a day. + // That would appear as midnight-midnight and would look dumb. + // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am) + if (seg.isStart || seg.isEnd) { + var zonedStart = calendar.msToMoment(seg.startMs); + var zonedEnd = calendar.msToMoment(seg.endMs); + timeText = this._getTimeText(zonedStart, zonedEnd, isAllDay); + fullTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, 'LT'); + startTimeText = this._getTimeText(zonedStart, zonedEnd, isAllDay, null, false); // displayEnd=false + } + } + else { + // Display the normal time text for the *event's* times + timeText = this.getTimeText(seg.footprint); + fullTimeText = this.getTimeText(seg.footprint, 'LT'); + startTimeText = this.getTimeText(seg.footprint, null, false); // displayEnd=false + } + return '' + + '
' + + (timeText ? + '
' + + '' + util_1.htmlEscape(timeText) + '' + + '
' : + '') + + (eventDef.title ? + '
' + + util_1.htmlEscape(eventDef.title) + + '
' : + '') + + '
' + + '
' + + /* TODO: write CSS for this + (isResizableFromStart ? + '
' : + '' + ) + + */ + (isResizableFromEnd ? + '
' : + '') + + ''; + }; + // Given segments that are assumed to all live in the *same column*, + // compute their verical/horizontal coordinates and assign to their elements. + TimeGridEventRenderer.prototype.updateFgSegCoords = function (segs) { + this.timeGrid.computeSegVerticals(segs); // horizontals relies on this + this.computeFgSegHorizontals(segs); // compute horizontal coordinates, z-index's, and reorder the array + this.timeGrid.assignSegVerticals(segs); + this.assignFgSegHorizontals(segs); + }; + // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each. + // NOTE: Also reorders the given array by date! + TimeGridEventRenderer.prototype.computeFgSegHorizontals = function (segs) { + var levels; + var level0; + var i; + this.sortEventSegs(segs); // order by certain criteria + levels = buildSlotSegLevels(segs); + computeForwardSlotSegs(levels); + if ((level0 = levels[0])) { + for (i = 0; i < level0.length; i++) { + computeSlotSegPressures(level0[i]); + } + for (i = 0; i < level0.length; i++) { + this.computeFgSegForwardBack(level0[i], 0, 0); + } + } + }; + // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range + // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and + // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left. + // + // The segment might be part of a "series", which means consecutive segments with the same pressure + // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of + // segments behind this one in the current series, and `seriesBackwardCoord` is the starting + // coordinate of the first segment in the series. + TimeGridEventRenderer.prototype.computeFgSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) { + var forwardSegs = seg.forwardSegs; + var i; + if (seg.forwardCoord === undefined) { + if (!forwardSegs.length) { + // if there are no forward segments, this segment should butt up against the edge + seg.forwardCoord = 1; + } + else { + // sort highest pressure first + this.sortForwardSegs(forwardSegs); + // this segment's forwardCoord will be calculated from the backwardCoord of the + // highest-pressure forward segment. + this.computeFgSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord); + seg.forwardCoord = forwardSegs[0].backwardCoord; + } + // calculate the backwardCoord from the forwardCoord. consider the series + seg.backwardCoord = seg.forwardCoord - + (seg.forwardCoord - seriesBackwardCoord) / // available width for series + (seriesBackwardPressure + 1); // # of segments in the series + // use this segment's coordinates to computed the coordinates of the less-pressurized + // forward segments + for (i = 0; i < forwardSegs.length; i++) { + this.computeFgSegForwardBack(forwardSegs[i], 0, seg.forwardCoord); + } + } + }; + TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) { + forwardSegs.sort(util_1.proxy(this, 'compareForwardSegs')); + }; + // A cmp function for determining which forward segment to rely on more when computing coordinates. + TimeGridEventRenderer.prototype.compareForwardSegs = function (seg1, seg2) { + // put higher-pressure first + return seg2.forwardPressure - seg1.forwardPressure || + // put segments that are closer to initial edge first (and favor ones with no coords yet) + (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) || + // do normal sorting... + this.compareEventSegs(seg1, seg2); + }; + // Given foreground event segments that have already had their position coordinates computed, + // assigns position-related CSS values to their elements. + TimeGridEventRenderer.prototype.assignFgSegHorizontals = function (segs) { + var i; + var seg; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + seg.el.css(this.generateFgSegHorizontalCss(seg)); + // if the height is short, add a className for alternate styling + if (seg.bottom - seg.top < 30) { + seg.el.addClass('fc-short'); + } + } + }; + // Generates an object with CSS properties/values that should be applied to an event segment element. + // Contains important positioning-related properties that should be applied to any event element, customized or not. + TimeGridEventRenderer.prototype.generateFgSegHorizontalCss = function (seg) { + var shouldOverlap = this.opt('slotEventOverlap'); + var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point + var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point + var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first + var isRTL = this.timeGrid.isRTL; + var left; // amount of space from left edge, a fraction of the total width + var right; // amount of space from right edge, a fraction of the total width + if (shouldOverlap) { + // double the width, but don't go beyond the maximum forward coordinate (1.0) + forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2); + } + if (isRTL) { + left = 1 - forwardCoord; + right = backwardCoord; + } + else { + left = backwardCoord; + right = 1 - forwardCoord; + } + props.zIndex = seg.level + 1; // convert from 0-base to 1-based + props.left = left * 100 + '%'; + props.right = right * 100 + '%'; + if (shouldOverlap && seg.forwardPressure) { + // add padding to the edge so that forward stacked events don't cover the resizer's icon + props[isRTL ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width + } + return props; + }; + return TimeGridEventRenderer; +}(EventRenderer_1.default)); +exports.default = TimeGridEventRenderer; +// Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is +// left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date. +function buildSlotSegLevels(segs) { + var levels = []; + var i; + var seg; + var j; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + // go through all the levels and stop on the first level where there are no collisions + for (j = 0; j < levels.length; j++) { + if (!computeSlotSegCollisions(seg, levels[j]).length) { + break; + } + } + seg.level = j; + (levels[j] || (levels[j] = [])).push(seg); + } + return levels; +} +// For every segment, figure out the other segments that are in subsequent +// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs +function computeForwardSlotSegs(levels) { + var i; + var level; + var j; + var seg; + var k; + for (i = 0; i < levels.length; i++) { + level = levels[i]; + for (j = 0; j < level.length; j++) { + seg = level[j]; + seg.forwardSegs = []; + for (k = i + 1; k < levels.length; k++) { + computeSlotSegCollisions(seg, levels[k], seg.forwardSegs); + } + } + } +} +// Figure out which path forward (via seg.forwardSegs) results in the longest path until +// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure +function computeSlotSegPressures(seg) { + var forwardSegs = seg.forwardSegs; + var forwardPressure = 0; + var i; + var forwardSeg; + if (seg.forwardPressure === undefined) { + for (i = 0; i < forwardSegs.length; i++) { + forwardSeg = forwardSegs[i]; + // figure out the child's maximum forward path + computeSlotSegPressures(forwardSeg); + // either use the existing maximum, or use the child's forward pressure + // plus one (for the forwardSeg itself) + forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure); + } + seg.forwardPressure = forwardPressure; + } +} +// Find all the segments in `otherSegs` that vertically collide with `seg`. +// Append into an optionally-supplied `results` array and return. +function computeSlotSegCollisions(seg, otherSegs, results) { + if (results === void 0) { results = []; } + for (var i = 0; i < otherSegs.length; i++) { + if (isSlotSegCollision(seg, otherSegs[i])) { + results.push(otherSegs[i]); + } + } + return results; +} +// Do these segments occupy the same vertical space? +function isSlotSegCollision(seg1, seg2) { + return seg1.bottom > seg2.top && seg1.top < seg2.bottom; +} + + +/***/ }), +/* 247 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var HelperRenderer_1 = __webpack_require__(58); +var TimeGridHelperRenderer = /** @class */ (function (_super) { + tslib_1.__extends(TimeGridHelperRenderer, _super); + function TimeGridHelperRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeGridHelperRenderer.prototype.renderSegs = function (segs, sourceSeg) { + var helperNodes = []; + var i; + var seg; + var sourceEl; + // TODO: not good to call eventRenderer this way + this.eventRenderer.renderFgSegsIntoContainers(segs, this.component.helperContainerEls); + // Try to make the segment that is in the same row as sourceSeg look the same + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + if (sourceSeg && sourceSeg.col === seg.col) { + sourceEl = sourceSeg.el; + seg.el.css({ + left: sourceEl.css('left'), + right: sourceEl.css('right'), + 'margin-left': sourceEl.css('margin-left'), + 'margin-right': sourceEl.css('margin-right') + }); + } + helperNodes.push(seg.el[0]); + } + return $(helperNodes); // must return the elements rendered + }; + return TimeGridHelperRenderer; +}(HelperRenderer_1.default)); +exports.default = TimeGridHelperRenderer; + + +/***/ }), +/* 248 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var FillRenderer_1 = __webpack_require__(57); +var TimeGridFillRenderer = /** @class */ (function (_super) { + tslib_1.__extends(TimeGridFillRenderer, _super); + function TimeGridFillRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + TimeGridFillRenderer.prototype.attachSegEls = function (type, segs) { + var timeGrid = this.component; + var containerEls; + // TODO: more efficient lookup + if (type === 'bgEvent') { + containerEls = timeGrid.bgContainerEls; + } + else if (type === 'businessHours') { + containerEls = timeGrid.businessContainerEls; + } + else if (type === 'highlight') { + containerEls = timeGrid.highlightContainerEls; + } + timeGrid.updateSegVerticals(segs); + timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls); + return segs.map(function (seg) { + return seg.el[0]; + }); + }; + return TimeGridFillRenderer; +}(FillRenderer_1.default)); +exports.default = TimeGridFillRenderer; + + +/***/ }), +/* 249 */ +/***/ (function(module, exports, __webpack_require__) { + +/* A rectangular panel that is absolutely positioned over other content +------------------------------------------------------------------------------------------------------------------------ +Options: + - className (string) + - content (HTML string or jQuery element set) + - parentEl + - top + - left + - right (the x coord of where the right edge should be. not a "CSS" right) + - autoHide (boolean) + - show (callback) + - hide (callback) +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var ListenerMixin_1 = __webpack_require__(7); +var Popover = /** @class */ (function () { + function Popover(options) { + this.isHidden = true; + this.margin = 10; // the space required between the popover and the edges of the scroll container + this.options = options || {}; + } + // Shows the popover on the specified position. Renders it if not already + Popover.prototype.show = function () { + if (this.isHidden) { + if (!this.el) { + this.render(); + } + this.el.show(); + this.position(); + this.isHidden = false; + this.trigger('show'); + } + }; + // Hides the popover, through CSS, but does not remove it from the DOM + Popover.prototype.hide = function () { + if (!this.isHidden) { + this.el.hide(); + this.isHidden = true; + this.trigger('hide'); + } + }; + // Creates `this.el` and renders content inside of it + Popover.prototype.render = function () { + var _this = this; + var options = this.options; + this.el = $('
') + .addClass(options.className || '') + .css({ + // position initially to the top left to avoid creating scrollbars + top: 0, + left: 0 + }) + .append(options.content) + .appendTo(options.parentEl); + // when a click happens on anything inside with a 'fc-close' className, hide the popover + this.el.on('click', '.fc-close', function () { + _this.hide(); + }); + if (options.autoHide) { + this.listenTo($(document), 'mousedown', this.documentMousedown); + } + }; + // Triggered when the user clicks *anywhere* in the document, for the autoHide feature + Popover.prototype.documentMousedown = function (ev) { + // only hide the popover if the click happened outside the popover + if (this.el && !$(ev.target).closest(this.el).length) { + this.hide(); + } + }; + // Hides and unregisters any handlers + Popover.prototype.removeElement = function () { + this.hide(); + if (this.el) { + this.el.remove(); + this.el = null; + } + this.stopListeningTo($(document), 'mousedown'); + }; + // Positions the popover optimally, using the top/left/right options + Popover.prototype.position = function () { + var options = this.options; + var origin = this.el.offsetParent().offset(); + var width = this.el.outerWidth(); + var height = this.el.outerHeight(); + var windowEl = $(window); + var viewportEl = util_1.getScrollParent(this.el); + var viewportTop; + var viewportLeft; + var viewportOffset; + var top; // the "position" (not "offset") values for the popover + var left; // + // compute top and left + top = options.top || 0; + if (options.left !== undefined) { + left = options.left; + } + else if (options.right !== undefined) { + left = options.right - width; // derive the left value from the right value + } + else { + left = 0; + } + if (viewportEl.is(window) || viewportEl.is(document)) { + viewportEl = windowEl; + viewportTop = 0; // the window is always at the top left + viewportLeft = 0; // (and .offset() won't work if called here) + } + else { + viewportOffset = viewportEl.offset(); + viewportTop = viewportOffset.top; + viewportLeft = viewportOffset.left; + } + // if the window is scrolled, it causes the visible area to be further down + viewportTop += windowEl.scrollTop(); + viewportLeft += windowEl.scrollLeft(); + // constrain to the view port. if constrained by two edges, give precedence to top/left + if (options.viewportConstrain !== false) { + top = Math.min(top, viewportTop + viewportEl.outerHeight() - height - this.margin); + top = Math.max(top, viewportTop + this.margin); + left = Math.min(left, viewportLeft + viewportEl.outerWidth() - width - this.margin); + left = Math.max(left, viewportLeft + this.margin); + } + this.el.css({ + top: top - origin.top, + left: left - origin.left + }); + }; + // Triggers a callback. Calls a function in the option hash of the same name. + // Arguments beyond the first `name` are forwarded on. + // TODO: better code reuse for this. Repeat code + Popover.prototype.trigger = function (name) { + if (this.options[name]) { + this.options[name].apply(this, Array.prototype.slice.call(arguments, 1)); + } + }; + return Popover; +}()); +exports.default = Popover; +ListenerMixin_1.default.mixInto(Popover); + + +/***/ }), +/* 250 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var util_1 = __webpack_require__(4); +var EventRenderer_1 = __webpack_require__(42); +/* Event-rendering methods for the DayGrid class +----------------------------------------------------------------------------------------------------------------------*/ +var DayGridEventRenderer = /** @class */ (function (_super) { + tslib_1.__extends(DayGridEventRenderer, _super); + function DayGridEventRenderer(dayGrid, fillRenderer) { + var _this = _super.call(this, dayGrid, fillRenderer) || this; + _this.dayGrid = dayGrid; + return _this; + } + DayGridEventRenderer.prototype.renderBgRanges = function (eventRanges) { + // don't render timed background events + eventRanges = $.grep(eventRanges, function (eventRange) { + return eventRange.eventDef.isAllDay(); + }); + _super.prototype.renderBgRanges.call(this, eventRanges); + }; + // Renders the given foreground event segments onto the grid + DayGridEventRenderer.prototype.renderFgSegs = function (segs) { + var rowStructs = this.rowStructs = this.renderSegRows(segs); + // append to each row's content skeleton + this.dayGrid.rowEls.each(function (i, rowNode) { + $(rowNode).find('.fc-content-skeleton > table').append(rowStructs[i].tbodyEl); + }); + }; + // Unrenders all currently rendered foreground event segments + DayGridEventRenderer.prototype.unrenderFgSegs = function () { + var rowStructs = this.rowStructs || []; + var rowStruct; + while ((rowStruct = rowStructs.pop())) { + rowStruct.tbodyEl.remove(); + } + this.rowStructs = null; + }; + // Uses the given events array to generate elements that should be appended to each row's content skeleton. + // Returns an array of rowStruct objects (see the bottom of `renderSegRow`). + // PRECONDITION: each segment shoud already have a rendered and assigned `.el` + DayGridEventRenderer.prototype.renderSegRows = function (segs) { + var rowStructs = []; + var segRows; + var row; + segRows = this.groupSegRows(segs); // group into nested arrays + // iterate each row of segment groupings + for (row = 0; row < segRows.length; row++) { + rowStructs.push(this.renderSegRow(row, segRows[row])); + } + return rowStructs; + }; + // Given a row # and an array of segments all in the same row, render a element, a skeleton that contains + // the segments. Returns object with a bunch of internal data about how the render was calculated. + // NOTE: modifies rowSegs + DayGridEventRenderer.prototype.renderSegRow = function (row, rowSegs) { + var colCnt = this.dayGrid.colCnt; + var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels + var levelCnt = Math.max(1, segLevels.length); // ensure at least one level + var tbody = $(''); + var segMatrix = []; // lookup for which segments are rendered into which level+col cells + var cellMatrix = []; // lookup for all elements of the level+col matrix + var loneCellMatrix = []; // lookup for elements that only take up a single column + var i; + var levelSegs; + var col; + var tr; + var j; + var seg; + var td; + // populates empty cells from the current column (`col`) to `endCol` + function emptyCellsUntil(endCol) { + while (col < endCol) { + // try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell + td = (loneCellMatrix[i - 1] || [])[col]; + if (td) { + td.attr('rowspan', parseInt(td.attr('rowspan') || 1, 10) + 1); + } + else { + td = $(''); + tr.append(td); + } + cellMatrix[i][col] = td; + loneCellMatrix[i][col] = td; + col++; + } + } + for (i = 0; i < levelCnt; i++) { + levelSegs = segLevels[i]; + col = 0; + tr = $(''); + segMatrix.push([]); + cellMatrix.push([]); + loneCellMatrix.push([]); + // levelCnt might be 1 even though there are no actual levels. protect against this. + // this single empty row is useful for styling. + if (levelSegs) { + for (j = 0; j < levelSegs.length; j++) { + seg = levelSegs[j]; + emptyCellsUntil(seg.leftCol); + // create a container that occupies or more columns. append the event element. + td = $('').append(seg.el); + if (seg.leftCol !== seg.rightCol) { + td.attr('colspan', seg.rightCol - seg.leftCol + 1); + } + else { + loneCellMatrix[i][col] = td; + } + while (col <= seg.rightCol) { + cellMatrix[i][col] = td; + segMatrix[i][col] = seg; + col++; + } + tr.append(td); + } + } + emptyCellsUntil(colCnt); // finish off the row + this.dayGrid.bookendCells(tr); + tbody.append(tr); + } + return { + row: row, + tbodyEl: tbody, + cellMatrix: cellMatrix, + segMatrix: segMatrix, + segLevels: segLevels, + segs: rowSegs + }; + }; + // Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels. + // NOTE: modifies segs + DayGridEventRenderer.prototype.buildSegLevels = function (segs) { + var levels = []; + var i; + var seg; + var j; + // Give preference to elements with certain criteria, so they have + // a chance to be closer to the top. + this.sortEventSegs(segs); + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + // loop through levels, starting with the topmost, until the segment doesn't collide with other segments + for (j = 0; j < levels.length; j++) { + if (!isDaySegCollision(seg, levels[j])) { + break; + } + } + // `j` now holds the desired subrow index + seg.level = j; + // create new level array if needed and append segment + (levels[j] || (levels[j] = [])).push(seg); + } + // order segments left-to-right. very important if calendar is RTL + for (j = 0; j < levels.length; j++) { + levels[j].sort(compareDaySegCols); + } + return levels; + }; + // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row + DayGridEventRenderer.prototype.groupSegRows = function (segs) { + var segRows = []; + var i; + for (i = 0; i < this.dayGrid.rowCnt; i++) { + segRows.push([]); + } + for (i = 0; i < segs.length; i++) { + segRows[segs[i].row].push(segs[i]); + } + return segRows; + }; + // Computes a default event time formatting string if `timeFormat` is not explicitly defined + DayGridEventRenderer.prototype.computeEventTimeFormat = function () { + return this.opt('extraSmallTimeFormat'); // like "6p" or "6:30p" + }; + // Computes a default `displayEventEnd` value if one is not expliclty defined + DayGridEventRenderer.prototype.computeDisplayEventEnd = function () { + return this.dayGrid.colCnt === 1; // we'll likely have space if there's only one day + }; + // Builds the HTML to be used for the default element for an individual segment + DayGridEventRenderer.prototype.fgSegHtml = function (seg, disableResizing) { + var view = this.view; + var eventDef = seg.footprint.eventDef; + var isAllDay = seg.footprint.componentFootprint.isAllDay; + var isDraggable = view.isEventDefDraggable(eventDef); + var isResizableFromStart = !disableResizing && isAllDay && + seg.isStart && view.isEventDefResizableFromStart(eventDef); + var isResizableFromEnd = !disableResizing && isAllDay && + seg.isEnd && view.isEventDefResizableFromEnd(eventDef); + var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd); + var skinCss = util_1.cssToStr(this.getSkinCss(eventDef)); + var timeHtml = ''; + var timeText; + var titleHtml; + classes.unshift('fc-day-grid-event', 'fc-h-event'); + // Only display a timed events time if it is the starting segment + if (seg.isStart) { + timeText = this.getTimeText(seg.footprint); + if (timeText) { + timeHtml = '' + util_1.htmlEscape(timeText) + ''; + } + } + titleHtml = + '' + + (util_1.htmlEscape(eventDef.title || '') || ' ') + // we always want one line of height + ''; + return '' + + '
' + + (this.dayGrid.isRTL ? + titleHtml + ' ' + timeHtml : // put a natural space in between + timeHtml + ' ' + titleHtml // + ) + + '
' + + (isResizableFromStart ? + '
' : + '') + + (isResizableFromEnd ? + '
' : + '') + + ''; + }; + return DayGridEventRenderer; +}(EventRenderer_1.default)); +exports.default = DayGridEventRenderer; +// Computes whether two segments' columns collide. They are assumed to be in the same row. +function isDaySegCollision(seg, otherSegs) { + var i; + var otherSeg; + for (i = 0; i < otherSegs.length; i++) { + otherSeg = otherSegs[i]; + if (otherSeg.leftCol <= seg.rightCol && + otherSeg.rightCol >= seg.leftCol) { + return true; + } + } + return false; +} +// A cmp function for determining the leftmost event +function compareDaySegCols(a, b) { + return a.leftCol - b.leftCol; +} + + +/***/ }), +/* 251 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var HelperRenderer_1 = __webpack_require__(58); +var DayGridHelperRenderer = /** @class */ (function (_super) { + tslib_1.__extends(DayGridHelperRenderer, _super); + function DayGridHelperRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null. + DayGridHelperRenderer.prototype.renderSegs = function (segs, sourceSeg) { + var helperNodes = []; + var rowStructs; + // TODO: not good to call eventRenderer this way + rowStructs = this.eventRenderer.renderSegRows(segs); + // inject each new event skeleton into each associated row + this.component.rowEls.each(function (row, rowNode) { + var rowEl = $(rowNode); // the .fc-row + var skeletonEl = $('
'); // will be absolutely positioned + var skeletonTopEl; + var skeletonTop; + // If there is an original segment, match the top position. Otherwise, put it at the row's top level + if (sourceSeg && sourceSeg.row === row) { + skeletonTop = sourceSeg.el.position().top; + } + else { + skeletonTopEl = rowEl.find('.fc-content-skeleton tbody'); + if (!skeletonTopEl.length) { + skeletonTopEl = rowEl.find('.fc-content-skeleton table'); + } + skeletonTop = skeletonTopEl.position().top; + } + skeletonEl.css('top', skeletonTop) + .find('table') + .append(rowStructs[row].tbodyEl); + rowEl.append(skeletonEl); + helperNodes.push(skeletonEl[0]); + }); + return $(helperNodes); // must return the elements rendered + }; + return DayGridHelperRenderer; +}(HelperRenderer_1.default)); +exports.default = DayGridHelperRenderer; + + +/***/ }), +/* 252 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var FillRenderer_1 = __webpack_require__(57); +var DayGridFillRenderer = /** @class */ (function (_super) { + tslib_1.__extends(DayGridFillRenderer, _super); + function DayGridFillRenderer() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.fillSegTag = 'td'; // override the default tag name + return _this; + } + DayGridFillRenderer.prototype.attachSegEls = function (type, segs) { + var nodes = []; + var i; + var seg; + var skeletonEl; + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + skeletonEl = this.renderFillRow(type, seg); + this.component.rowEls.eq(seg.row).append(skeletonEl); + nodes.push(skeletonEl[0]); + } + return nodes; + }; + // Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered. + DayGridFillRenderer.prototype.renderFillRow = function (type, seg) { + var colCnt = this.component.colCnt; + var startCol = seg.leftCol; + var endCol = seg.rightCol + 1; + var className; + var skeletonEl; + var trEl; + if (type === 'businessHours') { + className = 'bgevent'; + } + else { + className = type.toLowerCase(); + } + skeletonEl = $('
' + + '
' + + '
'); + trEl = skeletonEl.find('tr'); + if (startCol > 0) { + trEl.append(''); + } + trEl.append(seg.el.attr('colspan', endCol - startCol)); + if (endCol < colCnt) { + trEl.append(''); + } + this.component.bookendCells(trEl); + return skeletonEl; + }; + return DayGridFillRenderer; +}(FillRenderer_1.default)); +exports.default = DayGridFillRenderer; + + +/***/ }), +/* 253 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var BasicViewDateProfileGenerator_1 = __webpack_require__(228); +var UnzonedRange_1 = __webpack_require__(5); +var MonthViewDateProfileGenerator = /** @class */ (function (_super) { + tslib_1.__extends(MonthViewDateProfileGenerator, _super); + function MonthViewDateProfileGenerator() { + return _super !== null && _super.apply(this, arguments) || this; + } + // Computes the date range that will be rendered. + MonthViewDateProfileGenerator.prototype.buildRenderRange = function (currentUnzonedRange, currentRangeUnit, isRangeAllDay) { + var renderUnzonedRange = _super.prototype.buildRenderRange.call(this, currentUnzonedRange, currentRangeUnit, isRangeAllDay); + var start = this.msToUtcMoment(renderUnzonedRange.startMs, isRangeAllDay); + var end = this.msToUtcMoment(renderUnzonedRange.endMs, isRangeAllDay); + var rowCnt; + // ensure 6 weeks + if (this.opt('fixedWeekCount')) { + rowCnt = Math.ceil(// could be partial weeks due to hiddenDays + end.diff(start, 'weeks', true) // dontRound=true + ); + end.add(6 - rowCnt, 'weeks'); + } + return new UnzonedRange_1.default(start, end); + }; + return MonthViewDateProfileGenerator; +}(BasicViewDateProfileGenerator_1.default)); +exports.default = MonthViewDateProfileGenerator; + + +/***/ }), +/* 254 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var util_1 = __webpack_require__(4); +var EventRenderer_1 = __webpack_require__(42); +var ListEventRenderer = /** @class */ (function (_super) { + tslib_1.__extends(ListEventRenderer, _super); + function ListEventRenderer() { + return _super !== null && _super.apply(this, arguments) || this; + } + ListEventRenderer.prototype.renderFgSegs = function (segs) { + if (!segs.length) { + this.component.renderEmptyMessage(); + } + else { + this.component.renderSegList(segs); + } + }; + // generates the HTML for a single event row + ListEventRenderer.prototype.fgSegHtml = function (seg) { + var view = this.view; + var calendar = view.calendar; + var theme = calendar.theme; + var eventFootprint = seg.footprint; + var eventDef = eventFootprint.eventDef; + var componentFootprint = eventFootprint.componentFootprint; + var url = eventDef.url; + var classes = ['fc-list-item'].concat(this.getClasses(eventDef)); + var bgColor = this.getBgColor(eventDef); + var timeHtml; + if (componentFootprint.isAllDay) { + timeHtml = view.getAllDayHtml(); + } + else if (view.isMultiDayRange(componentFootprint.unzonedRange)) { + if (seg.isStart || seg.isEnd) { + timeHtml = util_1.htmlEscape(this._getTimeText(calendar.msToMoment(seg.startMs), calendar.msToMoment(seg.endMs), componentFootprint.isAllDay)); + } + else { + timeHtml = view.getAllDayHtml(); + } + } + else { + // Display the normal time text for the *event's* times + timeHtml = util_1.htmlEscape(this.getTimeText(eventFootprint)); + } + if (url) { + classes.push('fc-has-url'); + } + return '' + + (this.displayEventTime ? + '' + + (timeHtml || '') + + '' : + '') + + '' + + '' + + '' + + '' + + '' + + util_1.htmlEscape(eventDef.title || '') + + '' + + '' + + ''; + }; + // like "4:00am" + ListEventRenderer.prototype.computeEventTimeFormat = function () { + return this.opt('mediumTimeFormat'); + }; + return ListEventRenderer; +}(EventRenderer_1.default)); +exports.default = ListEventRenderer; + + +/***/ }), +/* 255 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var $ = __webpack_require__(3); +var EventPointing_1 = __webpack_require__(59); +var ListEventPointing = /** @class */ (function (_super) { + tslib_1.__extends(ListEventPointing, _super); + function ListEventPointing() { + return _super !== null && _super.apply(this, arguments) || this; + } + // for events with a url, the whole should be clickable, + // but it's impossible to wrap with an tag. simulate this. + ListEventPointing.prototype.handleClick = function (seg, ev) { + var url; + _super.prototype.handleClick.call(this, seg, ev); // might prevent the default action + // not clicking on or within an with an href + if (!$(ev.target).closest('a[href]').length) { + url = seg.footprint.eventDef.url; + if (url && !ev.isDefaultPrevented()) { + window.location.href = url; // simulate link click + } + } + }; + return ListEventPointing; +}(EventPointing_1.default)); +exports.default = ListEventPointing; + + +/***/ }), +/* 256 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var EventSourceParser_1 = __webpack_require__(38); +var ArrayEventSource_1 = __webpack_require__(52); +var FuncEventSource_1 = __webpack_require__(215); +var JsonFeedEventSource_1 = __webpack_require__(216); +EventSourceParser_1.default.registerClass(ArrayEventSource_1.default); +EventSourceParser_1.default.registerClass(FuncEventSource_1.default); +EventSourceParser_1.default.registerClass(JsonFeedEventSource_1.default); + + +/***/ }), +/* 257 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var ThemeRegistry_1 = __webpack_require__(51); +var StandardTheme_1 = __webpack_require__(213); +var JqueryUiTheme_1 = __webpack_require__(214); +var Bootstrap3Theme_1 = __webpack_require__(258); +var Bootstrap4Theme_1 = __webpack_require__(259); +ThemeRegistry_1.defineThemeSystem('standard', StandardTheme_1.default); +ThemeRegistry_1.defineThemeSystem('jquery-ui', JqueryUiTheme_1.default); +ThemeRegistry_1.defineThemeSystem('bootstrap3', Bootstrap3Theme_1.default); +ThemeRegistry_1.defineThemeSystem('bootstrap4', Bootstrap4Theme_1.default); + + +/***/ }), +/* 258 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var Theme_1 = __webpack_require__(19); +var Bootstrap3Theme = /** @class */ (function (_super) { + tslib_1.__extends(Bootstrap3Theme, _super); + function Bootstrap3Theme() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Bootstrap3Theme; +}(Theme_1.default)); +exports.default = Bootstrap3Theme; +Bootstrap3Theme.prototype.classes = { + widget: 'fc-bootstrap3', + tableGrid: 'table-bordered', + tableList: 'table', + tableListHeading: 'active', + buttonGroup: 'btn-group', + button: 'btn btn-default', + stateActive: 'active', + stateDisabled: 'disabled', + today: 'alert alert-info', + popover: 'panel panel-default', + popoverHeader: 'panel-heading', + popoverContent: 'panel-body', + // day grid + // for left/right border color when border is inset from edges (all-day in agenda view) + // avoid `panel` class b/c don't want margins/radius. only border color. + headerRow: 'panel-default', + dayRow: 'panel-default', + // list view + listView: 'panel panel-default' +}; +Bootstrap3Theme.prototype.baseIconClass = 'glyphicon'; +Bootstrap3Theme.prototype.iconClasses = { + close: 'glyphicon-remove', + prev: 'glyphicon-chevron-left', + next: 'glyphicon-chevron-right', + prevYear: 'glyphicon-backward', + nextYear: 'glyphicon-forward' +}; +Bootstrap3Theme.prototype.iconOverrideOption = 'bootstrapGlyphicons'; +Bootstrap3Theme.prototype.iconOverrideCustomButtonOption = 'bootstrapGlyphicon'; +Bootstrap3Theme.prototype.iconOverridePrefix = 'glyphicon-'; + + +/***/ }), +/* 259 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = __webpack_require__(2); +var Theme_1 = __webpack_require__(19); +var Bootstrap4Theme = /** @class */ (function (_super) { + tslib_1.__extends(Bootstrap4Theme, _super); + function Bootstrap4Theme() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Bootstrap4Theme; +}(Theme_1.default)); +exports.default = Bootstrap4Theme; +Bootstrap4Theme.prototype.classes = { + widget: 'fc-bootstrap4', + tableGrid: 'table-bordered', + tableList: 'table', + tableListHeading: 'table-active', + buttonGroup: 'btn-group', + button: 'btn btn-primary', + stateActive: 'active', + stateDisabled: 'disabled', + today: 'alert alert-info', + popover: 'card card-primary', + popoverHeader: 'card-header', + popoverContent: 'card-body', + // day grid + // for left/right border color when border is inset from edges (all-day in agenda view) + // avoid `table` class b/c don't want margins/padding/structure. only border color. + headerRow: 'table-bordered', + dayRow: 'table-bordered', + // list view + listView: 'card card-primary' +}; +Bootstrap4Theme.prototype.baseIconClass = 'fa'; +Bootstrap4Theme.prototype.iconClasses = { + close: 'fa-times', + prev: 'fa-chevron-left', + next: 'fa-chevron-right', + prevYear: 'fa-angle-double-left', + nextYear: 'fa-angle-double-right' +}; +Bootstrap4Theme.prototype.iconOverrideOption = 'bootstrapFontAwesome'; +Bootstrap4Theme.prototype.iconOverrideCustomButtonOption = 'bootstrapFontAwesome'; +Bootstrap4Theme.prototype.iconOverridePrefix = 'fa-'; + + +/***/ }), +/* 260 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var ViewRegistry_1 = __webpack_require__(22); +var BasicView_1 = __webpack_require__(62); +var MonthView_1 = __webpack_require__(229); +ViewRegistry_1.defineView('basic', { + 'class': BasicView_1.default +}); +ViewRegistry_1.defineView('basicDay', { + type: 'basic', + duration: { days: 1 } +}); +ViewRegistry_1.defineView('basicWeek', { + type: 'basic', + duration: { weeks: 1 } +}); +ViewRegistry_1.defineView('month', { + 'class': MonthView_1.default, + duration: { months: 1 }, + defaults: { + fixedWeekCount: true + } +}); + + +/***/ }), +/* 261 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var ViewRegistry_1 = __webpack_require__(22); +var AgendaView_1 = __webpack_require__(226); +ViewRegistry_1.defineView('agenda', { + 'class': AgendaView_1.default, + defaults: { + allDaySlot: true, + slotDuration: '00:30:00', + slotEventOverlap: true // a bad name. confused with overlap/constraint system + } +}); +ViewRegistry_1.defineView('agendaDay', { + type: 'agenda', + duration: { days: 1 } +}); +ViewRegistry_1.defineView('agendaWeek', { + type: 'agenda', + duration: { weeks: 1 } +}); + + +/***/ }), +/* 262 */ +/***/ (function(module, exports, __webpack_require__) { + +Object.defineProperty(exports, "__esModule", { value: true }); +var ViewRegistry_1 = __webpack_require__(22); +var ListView_1 = __webpack_require__(230); +ViewRegistry_1.defineView('list', { + 'class': ListView_1.default, + buttonTextKey: 'list', + defaults: { + buttonText: 'list', + listDayFormat: 'LL', + noEventsMessage: 'No events to display' + } +}); +ViewRegistry_1.defineView('listDay', { + type: 'list', + duration: { days: 1 }, + defaults: { + listDayFormat: 'dddd' // day-of-week is all we need. full date is probably in header + } +}); +ViewRegistry_1.defineView('listWeek', { + type: 'list', + duration: { weeks: 1 }, + defaults: { + listDayFormat: 'dddd', + listDayAltFormat: 'LL' + } +}); +ViewRegistry_1.defineView('listMonth', { + type: 'list', + duration: { month: 1 }, + defaults: { + listDayAltFormat: 'dddd' // day-of-week is nice-to-have + } +}); +ViewRegistry_1.defineView('listYear', { + type: 'list', + duration: { year: 1 }, + defaults: { + listDayAltFormat: 'dddd' // day-of-week is nice-to-have + } +}); + + +/***/ }), +/* 263 */ +/***/ (function(module, exports) { + +Object.defineProperty(exports, "__esModule", { value: true }); + + +/***/ }) +/******/ ]); +}); \ No newline at end of file diff --git a/public/lib/fc/fullcalendar.min.css b/public/lib/fc/fullcalendar.min.css new file mode 100644 index 0000000000..cf86d29318 --- /dev/null +++ b/public/lib/fc/fullcalendar.min.css @@ -0,0 +1,5 @@ +/*! + * FullCalendar v3.9.0 + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */.fc button,.fc table,body .fc{font-size:1em}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-icon,.fc-unselectable{-webkit-touch-callout:none;-khtml-user-select:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc .fc-row .fc-content-skeleton table,.fc .fc-row .fc-content-skeleton td,.fc .fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-icon,.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc.fc-bootstrap3 a,.ui-widget .fc-event{text-decoration:none}.fc-limited{display:none}.fc-icon,.fc-toolbar .fc-center{display:inline-block}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-bootstrap3 .fc-popover .panel-body,.fc-bootstrap4 .fc-popover .card-body{padding:0}.fc-now-indicator{position:absolute;border:0 solid red}.fc-bootstrap3 .fc-today.alert,.fc-bootstrap4 .fc-today.alert{border-radius:0}.fc-unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff;border-width:1px;border-style:solid}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-unthemed .fc-disabled-day{background:#d7d7d7;opacity:.3}.fc-icon{height:1em;line-height:1em;font-size:1em;font-family:"Courier New",Courier,monospace;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\2039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\D7";font-size:200%;top:6%}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666;font-size:.9em;margin-top:2px}.fc-unthemed .fc-list-item:hover td{background-color:#f5f5f5}.ui-widget .fc-disabled-day{background-image:none}.fc-bootstrap3 .fc-time-grid .fc-slats table,.fc-bootstrap4 .fc-time-grid .fc-slats table,.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-bootstrap3 hr.fc-divider,.fc-bootstrap4 hr.fc-divider{border-color:inherit}.ui-widget .fc-event{color:#fff;font-weight:400}.ui-widget td.fc-axis{font-weight:400}.fc.fc-bootstrap3 a[data-goto]:hover{text-decoration:underline}.fc.fc-bootstrap4 a{text-decoration:none}.fc.fc-bootstrap4 a[data-goto]:hover{text-decoration:underline}.fc-bootstrap4 a.fc-event:not([href]):not([tabindex]){color:#fff}.fc-bootstrap4 .fc-popover.card{position:absolute}.fc-toolbar.fc-header-toolbar{margin-bottom:1em}.fc-toolbar.fc-footer-toolbar{margin-top:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\A0-\A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee} \ No newline at end of file diff --git a/public/lib/fc/fullcalendar.min.js b/public/lib/fc/fullcalendar.min.js new file mode 100644 index 0000000000..88045457d9 --- /dev/null +++ b/public/lib/fc/fullcalendar.min.js @@ -0,0 +1,12 @@ +/*! + * FullCalendar v3.9.0 + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("moment"),require("jquery")):"function"==typeof define&&define.amd?define(["moment","jquery"],e):"object"==typeof exports?exports.FullCalendar=e(require("moment"),require("jquery")):t.FullCalendar=e(t.moment,t.jQuery)}("undefined"!=typeof self?self:this,function(t,e){return function(t){function e(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=236)}([function(e,n){e.exports=t},,function(t,e){var n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};e.__extends=function(t,e){function i(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(i.prototype=e.prototype,new i)}},function(t,n){t.exports=e},function(t,e,n){function i(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function r(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function o(){ht("body").addClass("fc-not-allowed")}function s(){ht("body").removeClass("fc-not-allowed")}function a(t,e,n){var i=Math.floor(e/t.length),r=Math.floor(e-i*(t.length-1)),o=[],s=[],a=[],u=0;l(t),t.each(function(e,n){var l=e===t.length-1?r:i,d=ht(n).outerHeight(!0);d *").each(function(t,n){var i=ht(n).outerWidth();i>e&&(e=i)}),e++,t.width(e),e}function d(t,e){var n,i=t.add(e);return i.css({position:"relative",left:-1}),n=t.outerHeight()-e.outerHeight(),i.css({position:"",left:""}),n}function c(t){var e=t.css("position"),n=t.parents().filter(function(){var t=ht(this);return/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==e&&n.length?n:ht(t[0].ownerDocument||document)}function p(t,e){var n=t.offset(),i=n.left-(e?e.left:0),r=n.top-(e?e.top:0);return{left:i,right:i+t.outerWidth(),top:r,bottom:r+t.outerHeight()}}function h(t,e){var n=t.offset(),i=g(t),r=n.left+b(t,"border-left-width")+i.left-(e?e.left:0),o=n.top+b(t,"border-top-width")+i.top-(e?e.top:0);return{left:r,right:r+t[0].clientWidth,top:o,bottom:o+t[0].clientHeight}}function f(t,e){var n=t.offset(),i=n.left+b(t,"border-left-width")+b(t,"padding-left")-(e?e.left:0),r=n.top+b(t,"border-top-width")+b(t,"padding-top")-(e?e.top:0);return{left:i,right:i+t.width(),top:r,bottom:r+t.height()}}function g(t){var e,n=t[0].offsetWidth-t[0].clientWidth,i=t[0].offsetHeight-t[0].clientHeight;return n=v(n),i=v(i),e={left:0,right:0,top:0,bottom:i},y()&&"rtl"===t.css("direction")?e.left=n:e.right=n,e}function v(t){return t=Math.max(0,t),t=Math.round(t)}function y(){return null===ft&&(ft=m()),ft}function m(){var t=ht("
").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),e=t.children(),n=e.offset().left>t.offset().left;return t.remove(),n}function b(t,e){return parseFloat(t.css(e))||0}function w(t){return 1===t.which&&!t.ctrlKey}function D(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageX:t.pageX}function E(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageY:t.pageY}function S(t){return/^touch/.test(t.type)}function C(t){t.addClass("fc-unselectable").on("selectstart",T)}function R(t){t.removeClass("fc-unselectable").off("selectstart",T)}function T(t){t.preventDefault()}function M(t,e){var n={left:Math.max(t.left,e.left),right:Math.min(t.right,e.right),top:Math.max(t.top,e.top),bottom:Math.min(t.bottom,e.bottom)};return n.left=1&&ut(o)));i++);return r}function L(t,e){var n=k(t);return"week"===n&&"object"==typeof e&&e.days&&(n="day"),n}function V(t,e,n){return null!=n?n.diff(e,t,!0):pt.isDuration(e)?e.as(t):e.end.diff(e.start,t,!0)}function G(t,e,n){var i;return U(n)?(e-t)/n:(i=n.asMonths(),Math.abs(i)>=1&&ut(i)?e.diff(t,"months",!0)/i:e.diff(t,"days",!0)/n.asDays())}function N(t,e){var n,i;return U(t)||U(e)?t/e:(n=t.asMonths(),i=e.asMonths(),Math.abs(n)>=1&&ut(n)&&Math.abs(i)>=1&&ut(i)?n/i:t.asDays()/e.asDays())}function j(t,e){var n;return U(t)?pt.duration(t*e):(n=t.asMonths(),Math.abs(n)>=1&&ut(n)?pt.duration({months:n*e}):pt.duration({days:t.asDays()*e}))}function U(t){return Boolean(t.hours()||t.minutes()||t.seconds()||t.milliseconds())}function W(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function q(t){return"string"==typeof t&&/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function Y(){for(var t=[],e=0;e=0;o--)if("object"==typeof(s=t[o][i]))r.unshift(s);else if(void 0!==s){l[i]=s;break}r.length&&(l[i]=Q(r))}for(n=t.length-1;n>=0;n--){a=t[n];for(i in a)i in l||(l[i]=a[i])}return l}function X(t,e){for(var n in t)$(t,n)&&(e[n]=t[n])}function $(t,e){return gt.call(t,e)}function K(t,e,n){if(ht.isFunction(t)&&(t=[t]),t){var i=void 0,r=void 0;for(i=0;i/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function rt(t){return t.replace(/&.*?;/g,"")}function ot(t){var e=[];return ht.each(t,function(t,n){null!=n&&e.push(t+":"+n)}),e.join(";")}function st(t){var e=[];return ht.each(t,function(t,n){null!=n&&e.push(t+'="'+it(n)+'"')}),e.join(" ")}function at(t){return t.charAt(0).toUpperCase()+t.slice(1)}function lt(t,e){return t-e}function ut(t){return t%1==0}function dt(t,e){var n=t[e];return function(){return n.apply(t,arguments)}}function ct(t,e,n){void 0===n&&(n=!1);var i,r,o,s,a,l=function(){var u=+new Date-s;ua&&s.push(new t(a,o.startMs)),o.endMs>a&&(a=o.endMs);return at.startMs)&&(null==this.startMs||null==t.endMs||this.startMs=this.startMs)&&(null==this.endMs||null!=t.endMs&&t.endMs<=this.endMs)},t.prototype.containsDate=function(t){var e=t.valueOf();return(null==this.startMs||e>=this.startMs)&&(null==this.endMs||e=this.endMs&&(e=this.endMs-1),e},t.prototype.equals=function(t){return this.startMs===t.startMs&&this.endMs===t.endMs},t.prototype.clone=function(){var e=new t(this.startMs,this.endMs);return e.isStart=this.isStart,e.isEnd=this.isEnd,e},t.prototype.getStart=function(){return null!=this.startMs?o.default.utc(this.startMs).stripZone():null},t.prototype.getEnd=function(){return null!=this.endMs?o.default.utc(this.endMs).stripZone():null},t.prototype.as=function(t){return r.utc(this.endMs).diff(r.utc(this.startMs),t,!0)},t}();e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(208),s=n(33),a=n(49),l=function(t){function e(n){var i=t.call(this)||this;return i.calendar=n,i.className=[],i.uid=String(e.uuid++),i}return i.__extends(e,t),e.parse=function(t,e){var n=new this(e);return!("object"!=typeof t||!n.applyProps(t))&&n},e.normalizeId=function(t){return t?String(t):null},e.prototype.fetch=function(t,e,n){},e.prototype.removeEventDefsById=function(t){},e.prototype.removeAllEventDefs=function(){},e.prototype.getPrimitive=function(t){},e.prototype.parseEventDefs=function(t){var e,n,i=[];for(e=0;e0},e}(o.default);e.default=s},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){this.isAllDay=!1,this.unzonedRange=t,this.isAllDay=e}return t.prototype.toLegacy=function(t){return{start:t.msToMoment(this.unzonedRange.startMs,this.isAllDay),end:t.msToMoment(this.unzonedRange.endMs,this.isAllDay)}},t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(34),o=n(209),s=n(17),a=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.buildInstances=function(){return[this.buildInstance()]},e.prototype.buildInstance=function(){return new o.default(this,this.dateProfile)},e.prototype.isAllDay=function(){return this.dateProfile.isAllDay()},e.prototype.clone=function(){var e=t.prototype.clone.call(this);return e.dateProfile=this.dateProfile,e},e.prototype.rezone=function(){var t=this.source.calendar,e=this.dateProfile;this.dateProfile=new s.default(t.moment(e.start),e.end?t.moment(e.end):null,t)},e.prototype.applyManualStandardProps=function(e){var n=t.prototype.applyManualStandardProps.call(this,e),i=s.default.parse(e,this.source);return!!i&&(this.dateProfile=i,null!=e.date&&(this.miscProps.date=e.date),n)},e}(r.default);e.default=a,a.defineStandardProps({start:!1,date:!1,end:!1,allDay:!1})},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(){}return t.mixInto=function(t){var e=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){t.prototype[n]||(t.prototype[n]=e.prototype[n])})},t.mixOver=function(t){var e=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){t.prototype[n]=e.prototype[n]})},t}();e.default=n},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t){this.view=t._getView(),this.component=t}return t.prototype.opt=function(t){return this.view.opt(t)},t.prototype.end=function(){},t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0}),e.version="3.9.0",e.internalApiVersion=12;var i=n(4);e.applyAll=i.applyAll,e.debounce=i.debounce,e.isInt=i.isInt,e.htmlEscape=i.htmlEscape,e.cssToStr=i.cssToStr,e.proxy=i.proxy,e.capitaliseFirstLetter=i.capitaliseFirstLetter,e.getOuterRect=i.getOuterRect,e.getClientRect=i.getClientRect,e.getContentRect=i.getContentRect,e.getScrollbarWidths=i.getScrollbarWidths,e.preventDefault=i.preventDefault,e.parseFieldSpecs=i.parseFieldSpecs,e.compareByFieldSpecs=i.compareByFieldSpecs,e.compareByFieldSpec=i.compareByFieldSpec,e.flexibleCompare=i.flexibleCompare,e.computeGreatestUnit=i.computeGreatestUnit,e.divideRangeByDuration=i.divideRangeByDuration,e.divideDurationByDuration=i.divideDurationByDuration,e.multiplyDuration=i.multiplyDuration,e.durationHasTime=i.durationHasTime,e.log=i.log,e.warn=i.warn,e.removeExact=i.removeExact,e.intersectRects=i.intersectRects;var r=n(47);e.formatDate=r.formatDate,e.formatRange=r.formatRange,e.queryMostGranularFormatUnit=r.queryMostGranularFormatUnit;var o=n(31);e.datepickerLocale=o.datepickerLocale,e.locale=o.locale;var s=n(10);e.moment=s.default;var a=n(11);e.EmitterMixin=a.default;var l=n(7);e.ListenerMixin=l.default;var u=n(48);e.Model=u.default;var d=n(207);e.Constraints=d.default;var c=n(5);e.UnzonedRange=c.default;var p=n(12);e.ComponentFootprint=p.default;var h=n(212);e.BusinessHourGenerator=h.default;var f=n(34);e.EventDef=f.default;var g=n(37);e.EventDefMutation=g.default;var v=n(38);e.EventSourceParser=v.default;var y=n(6);e.EventSource=y.default;var m=n(51);e.defineThemeSystem=m.defineThemeSystem;var b=n(18);e.EventInstanceGroup=b.default;var w=n(52);e.ArrayEventSource=w.default;var D=n(215);e.FuncEventSource=D.default;var E=n(216);e.JsonFeedEventSource=E.default;var S=n(36);e.EventFootprint=S.default;var C=n(33);e.Class=C.default;var R=n(14);e.Mixin=R.default;var T=n(53);e.CoordCache=T.default;var M=n(54);e.DragListener=M.default;var I=n(20);e.Promise=I.default;var H=n(217);e.TaskQueue=H.default;var P=n(218);e.RenderQueue=P.default;var _=n(39);e.Scroller=_.default;var x=n(19);e.Theme=x.default;var O=n(219);e.DateComponent=O.default;var F=n(40);e.InteractiveDateComponent=F.default;var z=n(220);e.Calendar=z.default;var B=n(41);e.View=B.default;var A=n(22);e.defineView=A.defineView,e.getViewConfig=A.getViewConfig;var k=n(55);e.DayTableMixin=k.default;var L=n(56);e.BusinessHourRenderer=L.default;var V=n(42);e.EventRenderer=V.default;var G=n(57);e.FillRenderer=G.default;var N=n(58);e.HelperRenderer=N.default;var j=n(222);e.ExternalDropping=j.default;var U=n(223);e.EventResizing=U.default;var W=n(59);e.EventPointing=W.default;var q=n(224);e.EventDragging=q.default;var Y=n(225);e.DateSelecting=Y.default;var Z=n(60);e.StandardInteractionsMixin=Z.default;var Q=n(226);e.AgendaView=Q.default;var X=n(227);e.TimeGrid=X.default;var $=n(61);e.DayGrid=$.default;var K=n(62);e.BasicView=K.default;var J=n(229);e.MonthView=J.default;var tt=n(230);e.ListView=tt.default},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),r=function(){function t(t,e,n){this.start=t,this.end=e||null,this.unzonedRange=this.buildUnzonedRange(n)}return t.parse=function(e,n){var i=e.start||e.date,r=e.end;if(!i)return!1;var o=n.calendar,s=o.moment(i),a=r?o.moment(r):null,l=e.allDay,u=o.opt("forceEventDuration");return!!s.isValid()&&(!a||a.isValid()&&a.isAfter(s)||(a=null),null==l&&null==(l=n.allDayDefault)&&(l=o.opt("allDayDefault")),!0===l?(s.stripTime(),a&&a.stripTime()):!1===l&&(s.hasTime()||s.time(0),a&&!a.hasTime()&&a.time(0)),!a&&u&&(a=o.getDefaultEventEnd(!s.hasTime(),s)),new t(s,a,o))},t.isStandardProp=function(t){return"start"===t||"date"===t||"end"===t||"allDay"===t},t.prototype.isAllDay=function(){return!(this.start.hasTime()||this.end&&this.end.hasTime())},t.prototype.buildUnzonedRange=function(t){var e=this.start.clone().stripZone().valueOf(),n=this.getEnd(t).stripZone().valueOf();return new i.default(e,n)},t.prototype.getEnd=function(t){return this.end?this.end.clone():t.getDefaultEventEnd(this.isAllDay(),this.start)},t}();e.default=r},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(5),r=n(35),o=n(211),s=function(){function t(t){this.eventInstances=t||[]}return t.prototype.getAllEventRanges=function(t){return t?this.sliceNormalRenderRanges(t):this.eventInstances.map(r.eventInstanceToEventRange)},t.prototype.sliceRenderRanges=function(t){return this.isInverse()?this.sliceInverseRenderRanges(t):this.sliceNormalRenderRanges(t)},t.prototype.sliceNormalRenderRanges=function(t){var e,n,i,r=this.eventInstances,s=[];for(e=0;e
')},e.prototype.clear=function(){this.setHeight("auto"),this.applyOverflow()},e.prototype.destroy=function(){this.el.remove()},e.prototype.applyOverflow=function(){this.scrollEl.css({"overflow-x":this.overflowX,"overflow-y":this.overflowY})},e.prototype.lockOverflow=function(t){var e=this.overflowX,n=this.overflowY;t=t||this.getScrollbarWidths(),"auto"===e&&(e=t.top||t.bottom||this.scrollEl[0].scrollWidth-1>this.scrollEl[0].clientWidth?"scroll":"hidden"),"auto"===n&&(n=t.left||t.right||this.scrollEl[0].scrollHeight-1>this.scrollEl[0].clientHeight?"scroll":"hidden"),this.scrollEl.css({"overflow-x":e,"overflow-y":n})},e.prototype.setHeight=function(t){this.scrollEl.height(t)},e.prototype.getScrollTop=function(){return this.scrollEl.scrollTop()},e.prototype.setScrollTop=function(t){this.scrollEl.scrollTop(t)},e.prototype.getClientWidth=function(){return this.scrollEl[0].clientWidth},e.prototype.getClientHeight=function(){return this.scrollEl[0].clientHeight},e.prototype.getScrollbarWidths=function(){return o.getScrollbarWidths(this.scrollEl)},e}(s.default);e.default=a},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(4),s=n(219),a=n(21),l=function(t){function e(e,n){var i=t.call(this,e,n)||this;return i.segSelector=".fc-event-container > *",i.dateSelectingClass&&(i.dateClicking=new i.dateClickingClass(i)),i.dateSelectingClass&&(i.dateSelecting=new i.dateSelectingClass(i)),i.eventPointingClass&&(i.eventPointing=new i.eventPointingClass(i)),i.eventDraggingClass&&i.eventPointing&&(i.eventDragging=new i.eventDraggingClass(i,i.eventPointing)),i.eventResizingClass&&i.eventPointing&&(i.eventResizing=new i.eventResizingClass(i,i.eventPointing)),i.externalDroppingClass&&(i.externalDropping=new i.externalDroppingClass(i)),i}return i.__extends(e,t),e.prototype.setElement=function(e){t.prototype.setElement.call(this,e),this.dateClicking&&this.dateClicking.bindToEl(e),this.dateSelecting&&this.dateSelecting.bindToEl(e),this.bindAllSegHandlersToEl(e)},e.prototype.removeElement=function(){this.endInteractions(),t.prototype.removeElement.call(this)},e.prototype.executeEventUnrender=function(){this.endInteractions(),t.prototype.executeEventUnrender.call(this)},e.prototype.bindGlobalHandlers=function(){t.prototype.bindGlobalHandlers.call(this),this.externalDropping&&this.externalDropping.bindToDocument()},e.prototype.unbindGlobalHandlers=function(){t.prototype.unbindGlobalHandlers.call(this),this.externalDropping&&this.externalDropping.unbindFromDocument()},e.prototype.bindDateHandlerToEl=function(t,e,n){var i=this;this.el.on(e,function(t){if(!r(t.target).is(i.segSelector+":not(.fc-helper),"+i.segSelector+":not(.fc-helper) *,.fc-more,a[data-goto]"))return n.call(i,t)})},e.prototype.bindAllSegHandlersToEl=function(t){[this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(e){e&&e.bindToEl(t)})},e.prototype.bindSegHandlerToEl=function(t,e,n){var i=this;t.on(e,this.segSelector,function(t){var e=r(t.currentTarget);if(!e.is(".fc-helper")){var o=e.data("fc-seg");if(o&&!i.shouldIgnoreEventPointing())return n.call(i,o,t)}})},e.prototype.shouldIgnoreMouse=function(){return a.default.get().shouldIgnoreMouse()},e.prototype.shouldIgnoreTouch=function(){var t=this._getView();return t.isSelected||t.selectedEvent},e.prototype.shouldIgnoreEventPointing=function(){return this.eventDragging&&this.eventDragging.isDragging||this.eventResizing&&this.eventResizing.isResizing},e.prototype.canStartSelection=function(t,e){return o.getEvIsTouch(e)&&!this.canStartResize(t,e)&&(this.isEventDefDraggable(t.footprint.eventDef)||this.isEventDefResizable(t.footprint.eventDef))},e.prototype.canStartDrag=function(t,e){return!this.canStartResize(t,e)&&this.isEventDefDraggable(t.footprint.eventDef)},e.prototype.canStartResize=function(t,e){var n=this._getView(),i=t.footprint.eventDef;return(!o.getEvIsTouch(e)||n.isEventDefSelected(i))&&this.isEventDefResizable(i)&&r(e.target).is(".fc-resizer")},e.prototype.endInteractions=function(){[this.dateClicking,this.dateSelecting,this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(t){t&&t.end()})},e.prototype.isEventDefDraggable=function(t){return this.isEventDefStartEditable(t)},e.prototype.isEventDefStartEditable=function(t){var e=t.isStartExplicitlyEditable();return null==e&&null==(e=this.opt("eventStartEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},e.prototype.isEventDefGenerallyEditable=function(t){var e=t.isExplicitlyEditable();return null==e&&(e=this.opt("editable")),e},e.prototype.isEventDefResizableFromStart=function(t){return this.opt("eventResizableFromStart")&&this.isEventDefResizable(t)},e.prototype.isEventDefResizableFromEnd=function(t){return this.isEventDefResizable(t)},e.prototype.isEventDefResizable=function(t){var e=t.isDurationExplicitlyEditable();return null==e&&null==(e=this.opt("eventDurationEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},e.prototype.diffDates=function(t,e){return this.largeUnit?o.diffByUnit(t,e,this.largeUnit):o.diffDayTime(t,e)},e.prototype.isEventInstanceGroupAllowed=function(t){var e,n=this._getView(),i=this.dateProfile,r=this.eventRangesToEventFootprints(t.getAllEventRanges());for(e=0;e1?"ll":"LL"},e.prototype.setDate=function(t){var e=this.get("dateProfile"),n=this.dateProfileGenerator.build(t,void 0,!0);e&&e.activeUnzonedRange.equals(n.activeUnzonedRange)||this.set("dateProfile",n)},e.prototype.unsetDate=function(){this.unset("dateProfile")},e.prototype.fetchInitialEvents=function(t){var e=this.calendar,n=t.isRangeAllDay&&!this.usesMinMaxTime;return e.requestEvents(e.msToMoment(t.activeUnzonedRange.startMs,n),e.msToMoment(t.activeUnzonedRange.endMs,n))},e.prototype.bindEventChanges=function(){this.listenTo(this.calendar,"eventsReset",this.resetEvents)},e.prototype.unbindEventChanges=function(){this.stopListeningTo(this.calendar,"eventsReset")},e.prototype.setEvents=function(t){this.set("currentEvents",t),this.set("hasEvents",!0)},e.prototype.unsetEvents=function(){this.unset("currentEvents"),this.unset("hasEvents")},e.prototype.resetEvents=function(t){this.startBatchRender(),this.unsetEvents(),this.setEvents(t),this.stopBatchRender()},e.prototype.requestDateRender=function(t){var e=this;this.requestRender(function(){e.executeDateRender(t)},"date","init")},e.prototype.requestDateUnrender=function(){var t=this;this.requestRender(function(){t.executeDateUnrender()},"date","destroy")},e.prototype.executeDateRender=function(e){t.prototype.executeDateRender.call(this,e),this.render&&this.render(),this.trigger("datesRendered"),this.addScroll({isDateInit:!0}),this.startNowIndicator()},e.prototype.executeDateUnrender=function(){this.unselect(),this.stopNowIndicator(),this.trigger("before:datesUnrendered"),this.destroy&&this.destroy(),t.prototype.executeDateUnrender.call(this)},e.prototype.bindBaseRenderHandlers=function(){var t=this;this.on("datesRendered",function(){t.whenSizeUpdated(t.triggerViewRender)}),this.on("before:datesUnrendered",function(){t.triggerViewDestroy()})},e.prototype.triggerViewRender=function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},e.prototype.triggerViewDestroy=function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},e.prototype.requestEventsRender=function(t){var e=this;this.requestRender(function(){e.executeEventRender(t),e.whenSizeUpdated(e.triggerAfterEventsRendered)},"event","init")},e.prototype.requestEventsUnrender=function(){var t=this;this.requestRender(function(){t.triggerBeforeEventsDestroyed(),t.executeEventUnrender()},"event","destroy")},e.prototype.requestBusinessHoursRender=function(t){var e=this;this.requestRender(function(){e.renderBusinessHours(t)},"businessHours","init")},e.prototype.requestBusinessHoursUnrender=function(){var t=this;this.requestRender(function(){t.unrenderBusinessHours()},"businessHours","destroy")},e.prototype.bindGlobalHandlers=function(){t.prototype.bindGlobalHandlers.call(this),this.listenTo(d.default.get(),{touchstart:this.processUnselect,mousedown:this.handleDocumentMousedown})},e.prototype.unbindGlobalHandlers=function(){t.prototype.unbindGlobalHandlers.call(this),this.stopListeningTo(d.default.get())},e.prototype.startNowIndicator=function(){var t,e,n,i=this;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit())&&(e=s.proxy(this,"updateNowIndicator"),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=(new Date).valueOf(),n=this.initialNowDate.clone().startOf(t).add(1,t).valueOf()-this.initialNowDate.valueOf(),this.nowIndicatorTimeoutID=setTimeout(function(){i.nowIndicatorTimeoutID=null,e(),n=+o.duration(1,t),n=Math.max(100,n),i.nowIndicatorIntervalID=setInterval(e,n)},n))},e.prototype.updateNowIndicator=function(){this.isDatesRendered&&this.initialNowDate&&(this.unrenderNowIndicator(),this.renderNowIndicator(this.initialNowDate.clone().add((new Date).valueOf()-this.initialNowQueriedMs)),this.isNowIndicatorRendered=!0)},e.prototype.stopNowIndicator=function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearInterval(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},e.prototype.updateSize=function(e,n,i){this.setHeight?this.setHeight(e,n):t.prototype.updateSize.call(this,e,n,i),this.updateNowIndicator()},e.prototype.addScroll=function(t){var e=this.queuedScroll||(this.queuedScroll={});r.extend(e,t)},e.prototype.popScroll=function(){this.applyQueuedScroll(),this.queuedScroll=null},e.prototype.applyQueuedScroll=function(){this.queuedScroll&&this.applyScroll(this.queuedScroll)},e.prototype.queryScroll=function(){var t={};return this.isDatesRendered&&r.extend(t,this.queryDateScroll()),t},e.prototype.applyScroll=function(t){t.isDateInit&&this.isDatesRendered&&r.extend(t,this.computeInitialDateScroll()),this.isDatesRendered&&this.applyDateScroll(t)},e.prototype.computeInitialDateScroll=function(){return{}},e.prototype.queryDateScroll=function(){return{}},e.prototype.applyDateScroll=function(t){},e.prototype.reportEventDrop=function(t,e,n,i){var r=this.calendar.eventManager,s=r.mutateEventsWithId(t.def.id,e),a=e.dateMutation;a&&(t.dateProfile=a.buildNewDateProfile(t.dateProfile,this.calendar)),this.triggerEventDrop(t,a&&a.dateDelta||o.duration(),s,n,i)},e.prototype.triggerEventDrop=function(t,e,n,i,r){this.publiclyTrigger("eventDrop",{context:i[0],args:[t.toLegacy(),e,n,r,{},this]})},e.prototype.reportExternalDrop=function(t,e,n,i,r,o){e&&this.calendar.eventManager.addEventDef(t,n),this.triggerExternalDrop(t,e,i,r,o)},e.prototype.triggerExternalDrop=function(t,e,n,i,r){this.publiclyTrigger("drop",{context:n[0],args:[t.dateProfile.start.clone(),i,r,this]}),e&&this.publiclyTrigger("eventReceive",{context:this,args:[t.buildInstance().toLegacy(),this]})},e.prototype.reportEventResize=function(t,e,n,i){var r=this.calendar.eventManager,o=r.mutateEventsWithId(t.def.id,e);t.dateProfile=e.dateMutation.buildNewDateProfile(t.dateProfile,this.calendar),this.triggerEventResize(t,e.dateMutation.endDelta,o,n,i)},e.prototype.triggerEventResize=function(t,e,n,i,r){this.publiclyTrigger("eventResize",{context:i[0],args:[t.toLegacy(),e,n,r,{},this]})},e.prototype.select=function(t,e){this.unselect(e),this.renderSelectionFootprint(t),this.reportSelection(t,e)},e.prototype.renderSelectionFootprint=function(e){this.renderSelection?this.renderSelection(e.toLegacy(this.calendar)):t.prototype.renderSelectionFootprint.call(this,e)},e.prototype.reportSelection=function(t,e){this.isSelected=!0,this.triggerSelect(t,e)},e.prototype.triggerSelect=function(t,e){var n=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("select",{context:this,args:[n.start,n.end,e,this]})},e.prototype.unselect=function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.publiclyTrigger("unselect",{context:this,args:[t,this]}))},e.prototype.selectEventInstance=function(t){this.selectedEventInstance&&this.selectedEventInstance===t||(this.unselectEventInstance(),this.getEventSegs().forEach(function(e){e.footprint.eventInstance===t&&e.el&&e.el.addClass("fc-selected")}),this.selectedEventInstance=t)},e.prototype.unselectEventInstance=function(){this.selectedEventInstance&&(this.getEventSegs().forEach(function(t){t.el&&t.el.removeClass("fc-selected")}),this.selectedEventInstance=null)},e.prototype.isEventDefSelected=function(t){return this.selectedEventInstance&&this.selectedEventInstance.def.id===t.id},e.prototype.handleDocumentMousedown=function(t){s.isPrimaryMouseButton(t)&&this.processUnselect(t)},e.prototype.processUnselect=function(t){this.processRangeUnselect(t),this.processEventUnselect(t)},e.prototype.processRangeUnselect=function(t){var e;this.isSelected&&this.opt("unselectAuto")&&((e=this.opt("unselectCancel"))&&r(t.target).closest(e).length||this.unselect(t))},e.prototype.processEventUnselect=function(t){this.selectedEventInstance&&(r(t.target).closest(".fc-selected").length||this.unselectEventInstance())},e.prototype.triggerBaseRendered=function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},e.prototype.triggerBaseUnrendered=function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},e.prototype.triggerDayClick=function(t,e,n){var i=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("dayClick",{context:e,args:[i.start,n,this]})},e.prototype.isDateInOtherMonth=function(t,e){return!1},e.prototype.getUnzonedRangeOption=function(t){var e=this.opt(t);if("function"==typeof e&&(e=e.apply(null,Array.prototype.slice.call(arguments,1))),e)return this.calendar.parseUnzonedRange(e)},e.prototype.initHiddenDays=function(){var t,e=this.opt("hiddenDays")||[],n=[],i=0;for(!1===this.opt("weekends")&&e.push(0,6),t=0;t<7;t++)(n[t]=-1!==r.inArray(t,e))||i++;if(!i)throw new Error("invalid hiddenDays");this.isHiddenDayHash=n},e.prototype.trimHiddenDays=function(t){var e=t.getStart(),n=t.getEnd();return e&&(e=this.skipHiddenDays(e)),n&&(n=this.skipHiddenDays(n,-1,!0)),null===e||null===n||eo&&(!l[s]||u.isSame(d,l[s]))&&(s-1!==o||"."!==c[s]);s--)v=c[s]+v;for(a=o;a<=s;a++)y+=c[a],m+=p[a];return(y||m)&&(b=r?m+i+y:y+i+m),g(h+b+v)}function a(t){return C[t]||(C[t]=l(t))}function l(t){var e=u(t);return{fakeFormatString:c(e),sameUnits:p(e)}}function u(t){for(var e,n=[],i=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=i.exec(t);)e[1]?n.push.apply(n,d(e[1])):e[2]?n.push({maybe:u(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push.apply(n,d(e[5]));return n}function d(t){return". "===t?["."," "]:[t]}function c(t){var e,n,i=[];for(e=0;er.value)&&(r=i);return r?r.unit:null}Object.defineProperty(e,"__esModule",{value:!0});var y=n(10);y.newMomentProto.format=function(){return this._fullCalendar&&arguments[0]?r(this,arguments[0]):this._ambigTime?y.oldMomentFormat(i(this),"YYYY-MM-DD"):this._ambigZone?y.oldMomentFormat(i(this),"YYYY-MM-DD[T]HH:mm:ss"):this._fullCalendar?y.oldMomentFormat(i(this)):y.oldMomentProto.format.apply(this,arguments)},y.newMomentProto.toISOString=function(){return this._ambigTime?y.oldMomentFormat(i(this),"YYYY-MM-DD"):this._ambigZone?y.oldMomentFormat(i(this),"YYYY-MM-DD[T]HH:mm:ss"):this._fullCalendar?y.oldMomentProto.toISOString.apply(i(this),arguments):y.oldMomentProto.toISOString.apply(this,arguments)};var m="\v",b="",w="",D=new RegExp(w+"([^"+w+"]*)"+w,"g"),E={t:function(t){return y.oldMomentFormat(t,"a").charAt(0)},T:function(t){return y.oldMomentFormat(t,"A").charAt(0)}},S={Y:{value:1,unit:"year"},M:{value:2,unit:"month"},W:{value:3,unit:"week"},w:{value:3,unit:"week"},D:{value:4,unit:"day"},d:{value:4,unit:"day"}};e.formatDate=r,e.formatRange=o;var C={};e.queryMostGranularFormatUnit=v},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(33),o=n(11),s=n(7),a=function(t){function e(){var e=t.call(this)||this;return e._watchers={},e._props={},e.applyGlobalWatchers(),e.constructed(),e}return i.__extends(e,t),e.watch=function(t){for(var e=[],n=1;n0&&(t=this.els.eq(0).offsetParent()),this.origin=t?t.offset():null,this.boundingRect=this.queryBoundingRect(),this.isHorizontal&&this.buildElHorizontals(),this.isVertical&&this.buildElVerticals()},t.prototype.clear=function(){this.origin=null,this.boundingRect=null,this.lefts=null,this.rights=null,this.tops=null,this.bottoms=null},t.prototype.ensureBuilt=function(){this.origin||this.build()},t.prototype.buildElHorizontals=function(){var t=[],e=[];this.els.each(function(n,r){var o=i(r),s=o.offset().left,a=o.outerWidth();t.push(s),e.push(s+a)}),this.lefts=t,this.rights=e},t.prototype.buildElVerticals=function(){var t=[],e=[];this.els.each(function(n,r){var o=i(r),s=o.offset().top,a=o.outerHeight();t.push(s),e.push(s+a)}),this.tops=t,this.bottoms=e},t.prototype.getHorizontalIndex=function(t){this.ensureBuilt();var e,n=this.lefts,i=this.rights,r=n.length;for(e=0;e=n[e]&&t=n[e]&&t0&&(t=r.getScrollParent(this.els.eq(0)),!t.is(document))?r.getClientRect(t):null},t.prototype.isPointInBounds=function(t,e){return this.isLeftInBounds(t)&&this.isTopInBounds(e)},t.prototype.isLeftInBounds=function(t){return!this.boundingRect||t>=this.boundingRect.left&&t=this.boundingRect.top&&t=i*i&&this.handleDistanceSurpassed(t),this.isDragging&&this.handleDrag(e,n,t)},t.prototype.handleDrag=function(t,e,n){this.trigger("drag",t,e,n),this.updateAutoScroll(n)},t.prototype.endDrag=function(t){this.isDragging&&(this.isDragging=!1,this.handleDragEnd(t))},t.prototype.handleDragEnd=function(t){this.trigger("dragEnd",t)},t.prototype.startDelay=function(t){var e=this;this.delay?this.delayTimeoutId=setTimeout(function(){e.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)},t.prototype.handleDelayEnd=function(t){this.isDelayEnded=!0,this.isDistanceSurpassed&&this.startDrag(t)},t.prototype.handleDistanceSurpassed=function(t){this.isDistanceSurpassed=!0,this.isDelayEnded&&this.startDrag(t)},t.prototype.handleTouchMove=function(t){this.isDragging&&this.shouldCancelTouchScroll&&t.preventDefault(),this.handleMove(t)},t.prototype.handleMouseMove=function(t){this.handleMove(t)},t.prototype.handleTouchScroll=function(t){this.isDragging&&!this.scrollAlwaysKills||this.endInteraction(t,!0)},t.prototype.trigger=function(t){for(var e=[],n=1;n=0&&e<=1?l=e*this.scrollSpeed*-1:n>=0&&n<=1&&(l=n*this.scrollSpeed),i>=0&&i<=1?u=i*this.scrollSpeed*-1:o>=0&&o<=1&&(u=o*this.scrollSpeed)),this.setScrollVel(l,u)},t.prototype.setScrollVel=function(t,e){this.scrollTopVel=t,this.scrollLeftVel=e,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(r.proxy(this,"scrollIntervalFunc"),this.scrollIntervalMs))},t.prototype.constrainScrollVel=function(){var t=this.scrollEl;this.scrollTopVel<0?t.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?t.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},t.prototype.scrollIntervalFunc=function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.endAutoScroll()},t.prototype.endAutoScroll=function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.handleScrollEnd())},t.prototype.handleDebouncedScroll=function(){this.scrollIntervalId||this.handleScrollEnd()},t.prototype.handleScrollEnd=function(){},t}();e.default=a,o.default.mixInto(a)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(4),o=n(14),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.updateDayTable=function(){for(var t,e,n,i=this,r=i.view,o=r.calendar,s=o.msToUtcMoment(i.dateProfile.renderUnzonedRange.startMs,!0),a=o.msToUtcMoment(i.dateProfile.renderUnzonedRange.endMs,!0),l=-1,u=[],d=[];s.isBefore(a);)r.isHiddenDay(s)?u.push(l+.5):(l++,u.push(l),d.push(s.clone())),s.add(1,"days");if(this.breakOnWeeks){for(e=d[0].day(),t=1;t=e.length?e[e.length-1]+1:e[n]},e.prototype.computeColHeadFormat=function(){return this.rowCnt>1||this.colCnt>10?"ddd":this.colCnt>1?this.opt("dayOfMonthFormat"):"dddd"},e.prototype.sliceRangeByRow=function(t){var e,n,i,r,o,s=this.daysPerRow,a=this.view.computeDayRange(t),l=this.getDateDayIndex(a.start),u=this.getDateDayIndex(a.end.clone().subtract(1,"days")),d=[];for(e=0;e'+this.renderHeadTrHtml()+"
"},e.prototype.renderHeadIntroHtml=function(){return this.renderIntroHtml()},e.prototype.renderHeadTrHtml=function(){return""+(this.isRTL?"":this.renderHeadIntroHtml())+this.renderHeadDateCellsHtml()+(this.isRTL?this.renderHeadIntroHtml():"")+""},e.prototype.renderHeadDateCellsHtml=function(){var t,e,n=[];for(t=0;t1?' colspan="'+e+'"':"")+(n?" "+n:"")+">"+(a?s.buildGotoAnchorHtml({date:t,forceOff:o.rowCnt>1||1===o.colCnt},i):i)+""},e.prototype.renderBgTrHtml=function(t){return""+(this.isRTL?"":this.renderBgIntroHtml(t))+this.renderBgCellsHtml(t)+(this.isRTL?this.renderBgIntroHtml(t):"")+""},e.prototype.renderBgIntroHtml=function(t){return this.renderIntroHtml()},e.prototype.renderBgCellsHtml=function(t){var e,n,i=[];for(e=0;e"},e.prototype.renderIntroHtml=function(){},e.prototype.bookendCells=function(t){var e=this.renderIntroHtml();e&&(this.isRTL?t.append(e):t.prepend(e))},e}(o.default);e.default=s},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){this.component=t,this.fillRenderer=e}return t.prototype.render=function(t){var e=this.component,n=e._getDateProfile().activeUnzonedRange,i=t.buildEventInstanceGroup(e.hasAllDayBusinessHours,n),r=i?e.eventRangesToEventFootprints(i.sliceRenderRanges(n)):[];this.renderEventFootprints(r)},t.prototype.renderEventFootprints=function(t){var e=this.component.eventFootprintsToSegs(t);this.renderSegs(e),this.segs=e},t.prototype.renderSegs=function(t){this.fillRenderer&&this.fillRenderer.renderSegs("businessHours",t,{getClasses:function(t){return["fc-nonbusiness","fc-bgevent"]}})},t.prototype.unrender=function(){this.fillRenderer&&this.fillRenderer.unrender("businessHours"),this.segs=null},t.prototype.getSegs=function(){return this.segs||[]},t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(3),r=n(4),o=function(){function t(t){this.fillSegTag="div",this.component=t,this.elsByFill={}}return t.prototype.renderFootprint=function(t,e,n){this.renderSegs(t,this.component.componentFootprintToSegs(e),n)},t.prototype.renderSegs=function(t,e,n){var i;return e=this.buildSegEls(t,e,n),i=this.attachSegEls(t,e),i&&this.reportEls(t,i),e},t.prototype.unrender=function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},t.prototype.buildSegEls=function(t,e,n){var r,o=this,s="",a=[];if(e.length){for(r=0;r"},t.prototype.attachSegEls=function(t,e){},t.prototype.reportEls=function(t,e){this.elsByFill[t]?this.elsByFill[t]=this.elsByFill[t].add(e):this.elsByFill[t]=i(e)},t}();e.default=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(13),r=n(36),o=n(6),s=function(){function t(t,e){this.view=t._getView(),this.component=t,this.eventRenderer=e}return t.prototype.renderComponentFootprint=function(t){this.renderEventFootprints([this.fabricateEventFootprint(t)])},t.prototype.renderEventDraggingFootprints=function(t,e,n){this.renderEventFootprints(t,e,"fc-dragging",n?null:this.view.opt("dragOpacity"))},t.prototype.renderEventResizingFootprints=function(t,e,n){this.renderEventFootprints(t,e,"fc-resizing")},t.prototype.renderEventFootprints=function(t,e,n,i){var r,o=this.component.eventFootprintsToSegs(t),s="fc-helper "+(n||"");for(o=this.eventRenderer.renderFgSegEls(o),r=0;r
'+this.renderBgTrHtml(t)+'
'+(this.getIsNumbersVisible()?""+this.renderNumberTrHtml(t)+"":"")+"
"},e.prototype.getIsNumbersVisible=function(){return this.getIsDayNumbersVisible()||this.cellWeekNumbersVisible},e.prototype.getIsDayNumbersVisible=function(){return this.rowCnt>1},e.prototype.renderNumberTrHtml=function(t){return""+(this.isRTL?"":this.renderNumberIntroHtml(t))+this.renderNumberCellsHtml(t)+(this.isRTL?this.renderNumberIntroHtml(t):"")+""},e.prototype.renderNumberIntroHtml=function(t){return this.renderIntroHtml()},e.prototype.renderNumberCellsHtml=function(t){var e,n,i=[];for(e=0;e",this.cellWeekNumbersVisible&&t.day()===n&&(r+=i.buildGotoAnchorHtml({date:t,type:"week"},{class:"fc-week-number"},t.format("w"))),s&&(r+=i.buildGotoAnchorHtml(t,{class:"fc-day-number"},t.format("D"))),r+=""):""},e.prototype.prepareHits=function(){this.colCoordCache.build(),this.rowCoordCache.build(),this.rowCoordCache.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},e.prototype.releaseHits=function(){this.colCoordCache.clear(),this.rowCoordCache.clear()},e.prototype.queryHit=function(t,e){if(this.colCoordCache.isLeftInBounds(t)&&this.rowCoordCache.isTopInBounds(e)){var n=this.colCoordCache.getHorizontalIndex(t),i=this.rowCoordCache.getVerticalIndex(e);if(null!=i&&null!=n)return this.getCellHit(i,n)}},e.prototype.getHitFootprint=function(t){var e=this.getCellRange(t.row,t.col);return new u.default(new l.default(e.start,e.end),!0)},e.prototype.getHitEl=function(t){return this.getCellEl(t.row,t.col)},e.prototype.getCellHit=function(t,e){return{row:t,col:e,component:this,left:this.colCoordCache.getLeftOffset(e),right:this.colCoordCache.getRightOffset(e),top:this.rowCoordCache.getTopOffset(t),bottom:this.rowCoordCache.getBottomOffset(t)}},e.prototype.getCellEl=function(t,e){return this.cellEls.eq(t*this.colCnt+e)},e.prototype.executeEventUnrender=function(){this.removeSegPopover(),t.prototype.executeEventUnrender.call(this)},e.prototype.getOwnEventSegs=function(){ +return t.prototype.getOwnEventSegs.call(this).concat(this.popoverSegs||[])},e.prototype.renderDrag=function(t,e,n){var i;for(i=0;i td > :first-child").each(e),i.position().top+o>a)return n;return!1},e.prototype.limitRow=function(t,e){var n,i,o,s,a,l,u,d,c,p,h,f,g,v,y,m=this,b=this.eventRenderer.rowStructs[t],w=[],D=0,E=function(n){for(;D").append(y),c.append(v),w.push(v[0])),D++};if(e&&e').attr("rowspan",p),l=d[f],y=this.renderMoreLink(t,a.leftCol+f,[a].concat(l)),v=r("
").append(y),g.append(v),h.push(g[0]),w.push(g[0]);c.addClass("fc-limited").after(r(h)),o.push(c[0])}}E(this.colCnt),b.moreEls=r(w),b.limitedEls=r(o)}},e.prototype.unlimitRow=function(t){var e=this.eventRenderer.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},e.prototype.renderMoreLink=function(t,e,n){var i=this,o=this.view;return r('').text(this.getMoreLinkText(n.length)).on("click",function(s){var a=i.opt("eventLimitClick"),l=i.getCellDate(t,e),u=r(s.currentTarget),d=i.getCellEl(t,e),c=i.getCellSegs(t,e),p=i.resliceDaySegs(c,l),h=i.resliceDaySegs(n,l);"function"==typeof a&&(a=i.publiclyTrigger("eventLimitClick",{context:o,args:[{date:l.clone(),dayEl:d,moreEl:u,segs:p,hiddenSegs:h},s,o]})),"popover"===a?i.showSegPopover(t,e,u,p):"string"==typeof a&&o.calendar.zoomTo(l,a)})},e.prototype.showSegPopover=function(t,e,n,i){var r,o,s=this,l=this.view,u=n.parent();r=1===this.rowCnt?l.el:this.rowEls.eq(t),o={className:"fc-more-popover "+l.calendar.theme.getClass("popover"),content:this.renderSegPopoverContent(t,e,i),parentEl:l.el,top:r.offset().top,autoHide:!0,viewportConstrain:this.opt("popoverViewportConstrain"),hide:function(){s.popoverSegs&&s.triggerBeforeEventSegsDestroyed(s.popoverSegs),s.segPopover.removeElement(),s.segPopover=null,s.popoverSegs=null}},this.isRTL?o.right=u.offset().left+u.outerWidth()+1:o.left=u.offset().left-1,this.segPopover=new a.default(o),this.segPopover.show(),this.bindAllSegHandlersToEl(this.segPopover.el),this.triggerAfterEventSegsRendered(i)},e.prototype.renderSegPopoverContent=function(t,e,n){var i,s=this.view,a=s.calendar.theme,l=this.getCellDate(t,e).format(this.opt("dayPopoverFormat")),u=r('
'+o.htmlEscape(l)+'
'),d=u.find(".fc-event-container");for(n=this.eventRenderer.renderFgSegEls(n,!0),this.popoverSegs=n,i=0;i"+s.htmlEscape(this.opt("weekNumberTitle"))+"":""},e.prototype.renderNumberIntroHtml=function(t){var e=this.view,n=this.getCellDate(t,0);return this.colWeekNumbersVisible?'"+e.buildGotoAnchorHtml({date:n,type:"week",forceOff:1===this.colCnt},n.format("w"))+"":""},e.prototype.renderBgIntroHtml=function(){var t=this.view;return this.colWeekNumbersVisible?'":""},e.prototype.renderIntroHtml=function(){var t=this.view;return this.colWeekNumbersVisible?'":""},e.prototype.getIsNumbersVisible=function(){return d.default.prototype.getIsNumbersVisible.apply(this,arguments)||this.colWeekNumbersVisible},e}(t)}Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),o=n(3),s=n(4),a=n(39),l=n(41),u=n(228),d=n(61),c=function(t){function e(e,n){var i=t.call(this,e,n)||this;return i.dayGrid=i.instantiateDayGrid(),i.dayGrid.isRigid=i.hasRigidRows(),i.opt("weekNumbers")&&(i.opt("weekNumbersWithinDays")?(i.dayGrid.cellWeekNumbersVisible=!0,i.dayGrid.colWeekNumbersVisible=!1):(i.dayGrid.cellWeekNumbersVisible=!1,i.dayGrid.colWeekNumbersVisible=!0)),i.addChild(i.dayGrid),i.scroller=new a.default({overflowX:"hidden",overflowY:"auto"}),i}return r.__extends(e,t),e.prototype.instantiateDayGrid=function(){return new(i(this.dayGridClass))(this)},e.prototype.executeDateRender=function(e){this.dayGrid.breakOnWeeks=/year|month|week/.test(e.currentRangeUnit),t.prototype.executeDateRender.call(this,e)},e.prototype.renderSkeleton=function(){var t,e;this.el.addClass("fc-basic-view").html(this.renderSkeletonHtml()),this.scroller.render(),t=this.scroller.el.addClass("fc-day-grid-container"),e=o('
').appendTo(t),this.el.find(".fc-body > tr > td").append(t),this.dayGrid.headContainerEl=this.el.find(".fc-head-container"),this.dayGrid.setElement(e)},e.prototype.unrenderSkeleton=function(){this.dayGrid.removeElement(),this.scroller.destroy()},e.prototype.renderSkeletonHtml=function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'},e.prototype.weekNumberStyleAttr=function(){return null!=this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},e.prototype.hasRigidRows=function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},e.prototype.updateSize=function(e,n,i){var r,o,a=this.opt("eventLimit"),l=this.dayGrid.headContainerEl.find(".fc-row");if(!this.dayGrid.rowEls)return void(n||(r=this.computeScrollerHeight(e),this.scroller.setHeight(r)));t.prototype.updateSize.call(this,e,n,i),this.dayGrid.colWeekNumbersVisible&&(this.weekNumberWidth=s.matchCellWidths(this.el.find(".fc-week-number"))),this.scroller.clear(),s.uncompensateScroll(l),this.dayGrid.removeSegPopover(),a&&"number"==typeof a&&this.dayGrid.limitRows(a),r=this.computeScrollerHeight(e),this.setGridHeight(r,n),a&&"number"!=typeof a&&this.dayGrid.limitRows(a),n||(this.scroller.setHeight(r),o=this.scroller.getScrollbarWidths(),(o.left||o.right)&&(s.compensateScroll(l,o),r=this.computeScrollerHeight(e),this.scroller.setHeight(r)),this.scroller.lockOverflow(o))},e.prototype.computeScrollerHeight=function(t){return t-s.subtractInnerElHeight(this.el,this.scroller.el)},e.prototype.setGridHeight=function(t,e){e?s.undistributeHeight(this.dayGrid.rowEls):s.distributeHeight(this.dayGrid.rowEls,t,!0)},e.prototype.computeInitialDateScroll=function(){return{top:0}},e.prototype.queryDateScroll=function(){return{top:this.scroller.getScrollTop()}},e.prototype.applyDateScroll=function(t){void 0!==t.top&&this.scroller.setScrollTop(t.top)},e}(l.default);e.default=c,c.prototype.dateProfileGeneratorClass=u.default,c.prototype.dayGridClass=d.default},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(t,e,n){function i(t,e,n){var i;for(i=0;i=0;e--)switch(n=i[e],n.type){case"init":r=!1;case"add":case"remove":i.splice(e,1)}return r&&i.push(t),r},e}(r.default);e.default=o},function(t,e,n){function i(t){var e,n,i,r=[];for(e in t)for(n=t[e].eventInstances,i=0;i'+n+"
":""+n+""},e.prototype.getAllDayHtml=function(){return this.opt("allDayHtml")||a.htmlEscape(this.opt("allDayText"))},e.prototype.getDayClasses=function(t,e){var n,i=this._getView(),r=[] +;return this.dateProfile.activeUnzonedRange.containsDate(t)?(r.push("fc-"+a.dayIDs[t.day()]),i.isDateInOtherMonth(t,this.dateProfile)&&r.push("fc-other-month"),n=i.calendar.getNow(),t.isSame(n,"day")?(r.push("fc-today"),!0!==e&&r.push(i.calendar.theme.getClass("today"))):t=this.nextDayThreshold&&o.add(1,"days"),o<=n&&(o=n.clone().add(1,"days")),{start:n,end:o}},e.prototype.isMultiDayRange=function(t){var e=this.computeDayRange(t);return e.end.diff(e.start,"days")>1},e.guid=0,e}(d.default);e.default=p},function(t,e,n){function i(t,e){return null==e?t:r.isFunction(e)?t.filter(e):(e+="",t.filter(function(t){return t.id==e||t._id===e}))}Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),o=n(0),s=n(4),a=n(32),l=n(238),u=n(21),d=n(11),c=n(7),p=n(239),h=n(240),f=n(241),g=n(207),v=n(31),y=n(10),m=n(5),b=n(12),w=n(17),D=n(242),E=n(212),S=n(38),C=n(49),R=n(13),T=n(37),M=n(6),I=n(51),H=function(){function t(t,e){this.loadingLevel=0,this.ignoreUpdateViewSize=0,this.freezeContentHeightDepth=0,u.default.needed(),this.el=t,this.viewsByType={},this.optionsManager=new h.default(this,e),this.viewSpecManager=new f.default(this.optionsManager,this),this.initMomentInternals(),this.initCurrentDate(),this.initEventManager(),this.constraints=new g.default(this.eventManager,this),this.constructed()}return t.prototype.constructed=function(){},t.prototype.getView=function(){return this.view},t.prototype.publiclyTrigger=function(t,e){var n,i,o=this.opt(t);if(r.isPlainObject(e)?(n=e.context,i=e.args):r.isArray(e)&&(i=e),null==n&&(n=this.el[0]),i||(i=[]),this.triggerWith(t,n,i),o)return o.apply(n,i)},t.prototype.hasPublicHandlers=function(t){return this.hasHandlers(t)||this.opt(t)},t.prototype.option=function(t,e){var n;if("string"==typeof t){if(void 0===e)return this.optionsManager.get(t);n={},n[t]=e,this.optionsManager.add(n)}else"object"==typeof t&&this.optionsManager.add(t)},t.prototype.opt=function(t){return this.optionsManager.get(t)},t.prototype.instantiateView=function(t){var e=this.viewSpecManager.getViewSpec(t);if(!e)throw new Error('View type "'+t+'" is not valid');return new e.class(this,e)},t.prototype.isValidViewType=function(t){return Boolean(this.viewSpecManager.getViewSpec(t))},t.prototype.changeView=function(t,e){e&&(e.start&&e.end?this.optionsManager.recordOverrides({visibleRange:e}):this.currentDate=this.moment(e).stripZone()),this.renderView(t)},t.prototype.zoomTo=function(t,e){var n;e=e||"day",n=this.viewSpecManager.getViewSpec(e)||this.viewSpecManager.getUnitViewSpec(e),this.currentDate=t.clone(),this.renderView(n?n.type:null)},t.prototype.initCurrentDate=function(){var t=this.opt("defaultDate");this.currentDate=null!=t?this.moment(t).stripZone():this.getNow()},t.prototype.prev=function(){var t=this.view,e=t.dateProfileGenerator.buildPrev(t.get("dateProfile"));e.isValid&&(this.currentDate=e.date,this.renderView())},t.prototype.next=function(){var t=this.view,e=t.dateProfileGenerator.buildNext(t.get("dateProfile"));e.isValid&&(this.currentDate=e.date,this.renderView())},t.prototype.prevYear=function(){this.currentDate.add(-1,"years"),this.renderView()},t.prototype.nextYear=function(){this.currentDate.add(1,"years"),this.renderView()},t.prototype.today=function(){this.currentDate=this.getNow(),this.renderView()},t.prototype.gotoDate=function(t){this.currentDate=this.moment(t).stripZone(),this.renderView()},t.prototype.incrementDate=function(t){this.currentDate.add(o.duration(t)),this.renderView()},t.prototype.getDate=function(){return this.applyTimezone(this.currentDate)},t.prototype.pushLoading=function(){this.loadingLevel++||this.publiclyTrigger("loading",[!0,this.view])},t.prototype.popLoading=function(){--this.loadingLevel||this.publiclyTrigger("loading",[!1,this.view])},t.prototype.render=function(){this.contentEl?this.elementVisible()&&(this.calcSize(),this.updateViewSize()):this.initialRender()},t.prototype.initialRender=function(){var t=this,e=this.el;e.addClass("fc"),e.on("click.fc","a[data-goto]",function(e){var n=r(e.currentTarget),i=n.data("goto"),o=t.moment(i.date),a=i.type,l=t.view.opt("navLink"+s.capitaliseFirstLetter(a)+"Click");"function"==typeof l?l(o,e):("string"==typeof l&&(a=l),t.zoomTo(o,a))}),this.optionsManager.watch("settingTheme",["?theme","?themeSystem"],function(n){var i=I.getThemeSystemClass(n.themeSystem||n.theme),r=new i(t.optionsManager),o=r.getClass("widget");t.theme=r,o&&e.addClass(o)},function(){var n=t.theme.getClass("widget");t.theme=null,n&&e.removeClass(n)}),this.optionsManager.watch("settingBusinessHourGenerator",["?businessHours"],function(e){t.businessHourGenerator=new E.default(e.businessHours,t),t.view&&t.view.set("businessHourGenerator",t.businessHourGenerator)},function(){t.businessHourGenerator=null}),this.optionsManager.watch("applyingDirClasses",["?isRTL","?locale"],function(t){e.toggleClass("fc-ltr",!t.isRTL),e.toggleClass("fc-rtl",t.isRTL)}),this.contentEl=r("
").prependTo(e),this.initToolbars(),this.renderHeader(),this.renderFooter(),this.renderView(this.opt("defaultView")),this.opt("handleWindowResize")&&r(window).resize(this.windowResizeProxy=s.debounce(this.windowResize.bind(this),this.opt("windowResizeDelay")))},t.prototype.destroy=function(){this.view&&this.clearView(),this.toolbarsManager.proxyCall("removeElement"),this.contentEl.remove(),this.el.removeClass("fc fc-ltr fc-rtl"),this.optionsManager.unwatch("settingTheme"),this.optionsManager.unwatch("settingBusinessHourGenerator"),this.el.off(".fc"),this.windowResizeProxy&&(r(window).unbind("resize",this.windowResizeProxy),this.windowResizeProxy=null),u.default.unneeded()},t.prototype.elementVisible=function(){return this.el.is(":visible")},t.prototype.bindViewHandlers=function(t){var e=this;t.watch("titleForCalendar",["title"],function(n){t===e.view&&e.setToolbarsTitle(n.title)}),t.watch("dateProfileForCalendar",["dateProfile"],function(n){t===e.view&&(e.currentDate=n.dateProfile.date,e.updateToolbarButtons(n.dateProfile))})},t.prototype.unbindViewHandlers=function(t){t.unwatch("titleForCalendar"),t.unwatch("dateProfileForCalendar")},t.prototype.renderView=function(t){var e,n=this.view;this.freezeContentHeight(),n&&t&&n.type!==t&&this.clearView(),!this.view&&t&&(e=this.view=this.viewsByType[t]||(this.viewsByType[t]=this.instantiateView(t)),this.bindViewHandlers(e),e.startBatchRender(),e.setElement(r("
").appendTo(this.contentEl)),this.toolbarsManager.proxyCall("activateButton",t)),this.view&&(this.view.get("businessHourGenerator")!==this.businessHourGenerator&&this.view.set("businessHourGenerator",this.businessHourGenerator),this.view.setDate(this.currentDate),e&&e.stopBatchRender()),this.thawContentHeight()},t.prototype.clearView=function(){var t=this.view;this.toolbarsManager.proxyCall("deactivateButton",t.type),this.unbindViewHandlers(t),t.removeElement(),t.unsetDate(),this.view=null},t.prototype.reinitView=function(){var t=this.view,e=t.queryScroll();this.freezeContentHeight(),this.clearView(),this.calcSize(),this.renderView(t.type),this.view.applyScroll(e),this.thawContentHeight()},t.prototype.getSuggestedViewHeight=function(){return null==this.suggestedViewHeight&&this.calcSize(),this.suggestedViewHeight},t.prototype.isHeightAuto=function(){return"auto"===this.opt("contentHeight")||"auto"===this.opt("height")},t.prototype.updateViewSize=function(t){void 0===t&&(t=!1);var e,n=this.view;if(!this.ignoreUpdateViewSize&&n)return t&&(this.calcSize(),e=n.queryScroll()),this.ignoreUpdateViewSize++,n.updateSize(this.getSuggestedViewHeight(),this.isHeightAuto(),t),this.ignoreUpdateViewSize--,t&&n.applyScroll(e),!0},t.prototype.calcSize=function(){this.elementVisible()&&this._calcSize()},t.prototype._calcSize=function(){var t=this.opt("contentHeight"),e=this.opt("height");this.suggestedViewHeight="number"==typeof t?t:"function"==typeof t?t():"number"==typeof e?e-this.queryToolbarsHeight():"function"==typeof e?e()-this.queryToolbarsHeight():"parent"===e?this.el.parent().height()-this.queryToolbarsHeight():Math.round(this.contentEl.width()/Math.max(this.opt("aspectRatio"),.5))},t.prototype.windowResize=function(t){t.target===window&&this.view&&this.view.isDatesRendered&&this.updateViewSize(!0)&&this.publiclyTrigger("windowResize",[this.view])},t.prototype.freezeContentHeight=function(){this.freezeContentHeightDepth++||this.forceFreezeContentHeight()},t.prototype.forceFreezeContentHeight=function(){this.contentEl.css({width:"100%",height:this.contentEl.height(),overflow:"hidden"})},t.prototype.thawContentHeight=function(){this.freezeContentHeightDepth--,this.contentEl.css({width:"",height:"",overflow:""}),this.freezeContentHeightDepth&&this.forceFreezeContentHeight()},t.prototype.initToolbars=function(){this.header=new p.default(this,this.computeHeaderOptions()),this.footer=new p.default(this,this.computeFooterOptions()),this.toolbarsManager=new l.default([this.header,this.footer])},t.prototype.computeHeaderOptions=function(){return{extraClasses:"fc-header-toolbar",layout:this.opt("header")}},t.prototype.computeFooterOptions=function(){return{extraClasses:"fc-footer-toolbar",layout:this.opt("footer")}},t.prototype.renderHeader=function(){var t=this.header;t.setToolbarOptions(this.computeHeaderOptions()),t.render(),t.el&&this.el.prepend(t.el)},t.prototype.renderFooter=function(){var t=this.footer;t.setToolbarOptions(this.computeFooterOptions()),t.render(),t.el&&this.el.append(t.el)},t.prototype.setToolbarsTitle=function(t){this.toolbarsManager.proxyCall("updateTitle",t)},t.prototype.updateToolbarButtons=function(t){var e=this.getNow(),n=this.view,i=n.dateProfileGenerator.build(e),r=n.dateProfileGenerator.buildPrev(n.get("dateProfile")),o=n.dateProfileGenerator.buildNext(n.get("dateProfile"));this.toolbarsManager.proxyCall(i.isValid&&!t.currentUnzonedRange.containsDate(e)?"enableButton":"disableButton","today"),this.toolbarsManager.proxyCall(r.isValid?"enableButton":"disableButton","prev"),this.toolbarsManager.proxyCall(o.isValid?"enableButton":"disableButton","next")},t.prototype.queryToolbarsHeight=function(){return this.toolbarsManager.items.reduce(function(t,e){return t+(e.el?e.el.outerHeight(!0):0)},0)},t.prototype.select=function(t,e){this.view.select(this.buildSelectFootprint.apply(this,arguments))},t.prototype.unselect=function(){this.view&&this.view.unselect()},t.prototype.buildSelectFootprint=function(t,e){var n,i=this.moment(t).stripZone();return n=e?this.moment(e).stripZone():i.hasTime()?i.clone().add(this.defaultTimedEventDuration):i.clone().add(this.defaultAllDayEventDuration),new b.default(new m.default(i,n),!i.hasTime())},t.prototype.initMomentInternals=function(){var t=this;this.defaultAllDayEventDuration=o.duration(this.opt("defaultAllDayEventDuration")),this.defaultTimedEventDuration=o.duration(this.opt("defaultTimedEventDuration")),this.optionsManager.watch("buildingMomentLocale",["?locale","?monthNames","?monthNamesShort","?dayNames","?dayNamesShort","?firstDay","?weekNumberCalculation"],function(e){var n,i=e.weekNumberCalculation,r=e.firstDay;"iso"===i&&(i="ISO");var o=Object.create(v.getMomentLocaleData(e.locale));e.monthNames&&(o._months=e.monthNames),e.monthNamesShort&&(o._monthsShort=e.monthNamesShort),e.dayNames&&(o._weekdays=e.dayNames),e.dayNamesShort&&(o._weekdaysShort=e.dayNamesShort),null==r&&"ISO"===i&&(r=1),null!=r&&(n=Object.create(o._week),n.dow=r,o._week=n),"ISO"!==i&&"local"!==i&&"function"!=typeof i||(o._fullCalendar_weekCalc=i),t.localeData=o,t.currentDate&&t.localizeMoment(t.currentDate)})},t.prototype.moment=function(){for(var t=[],e=0;e864e5&&r.time(n-864e5)),new o.default(i,r)},t.prototype.buildRangeFromDuration=function(t,e,n,s){function a(){d=t.clone().startOf(h),c=d.clone().add(n),p=new o.default(d,c)}var l,u,d,c,p,h=this.opt("dateAlignment");return h||(l=this.opt("dateIncrement"),l?(u=i.duration(l),h=uo.getStart()&&(i=new a.default,i.setEndDelta(l),r=new s.default,r.setDateMutation(i),r)},e}(u.default);e.default=d},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(4),o=n(37),s=n(50),a=n(54),l=n(23),u=n(244),d=n(15),c=function(t){function e(e,n){var i=t.call(this,e)||this;return i.isDragging=!1,i.eventPointing=n,i}return i.__extends(e,t),e.prototype.end=function(){this.dragListener&&this.dragListener.endInteraction()},e.prototype.getSelectionDelay=function(){var t=this.opt("eventLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},e.prototype.bindToEl=function(t){var e=this.component;e.bindSegHandlerToEl(t,"mousedown",this.handleMousedown.bind(this)),e.bindSegHandlerToEl(t,"touchstart",this.handleTouchStart.bind(this))},e.prototype.handleMousedown=function(t,e){!this.component.shouldIgnoreMouse()&&this.component.canStartDrag(t,e)&&this.buildDragListener(t).startInteraction(e,{distance:5})},e.prototype.handleTouchStart=function(t,e){var n=this.component,i={delay:this.view.isEventDefSelected(t.footprint.eventDef)?0:this.getSelectionDelay()};n.canStartDrag(t,e)?this.buildDragListener(t).startInteraction(e,i):n.canStartSelection(t,e)&&this.buildSelectListener(t).startInteraction(e,i)},e.prototype.buildSelectListener=function(t){var e=this,n=this.view,i=t.footprint.eventDef,r=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var o=this.dragListener=new a.default({dragStart:function(t){o.isTouch&&!n.isEventDefSelected(i)&&r&&n.selectEventInstance(r)},interactionEnd:function(t){e.dragListener=null}});return o},e.prototype.buildDragListener=function(t){var e,n,i,o=this,s=this.component,a=this.view,d=a.calendar,c=d.eventManager,p=t.el,h=t.footprint.eventDef,f=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var g=this.dragListener=new l.default(a,{scroll:this.opt("dragScroll"),subjectEl:p,subjectCenter:!0,interactionStart:function(i){t.component=s,e=!1,n=new u.default(t.el,{additionalClass:"fc-dragging",parentEl:a.el,opacity:g.isTouch?null:o.opt("dragOpacity"),revertDuration:o.opt("dragRevertDuration"),zIndex:2}),n.hide(),n.start(i)},dragStart:function(n){g.isTouch&&!a.isEventDefSelected(h)&&f&&a.selectEventInstance(f),e=!0,o.eventPointing.handleMouseout(t,n),o.segDragStart(t,n),a.hideEventsWithId(t.footprint.eventDef.id)},hitOver:function(e,l,u){var p,f,v,y=!0;t.hit&&(u=t.hit),p=u.component.getSafeHitFootprint(u),f=e.component.getSafeHitFootprint(e),p&&f?(i=o.computeEventDropMutation(p,f,h),i?(v=c.buildMutatedEventInstanceGroup(h.id,i),y=s.isEventInstanceGroupAllowed(v)):y=!1):y=!1,y||(i=null,r.disableCursor()),i&&a.renderDrag(s.eventRangesToEventFootprints(v.sliceRenderRanges(s.dateProfile.renderUnzonedRange,d)),t,g.isTouch)?n.hide():n.show(),l&&(i=null)},hitOut:function(){a.unrenderDrag(t),n.show(),i=null},hitDone:function(){r.enableCursor()},interactionEnd:function(r){delete t.component,n.stop(!i,function(){e&&(a.unrenderDrag(t),o.segDragStop(t,r)),a.showEventsWithId(t.footprint.eventDef.id),i&&a.reportEventDrop(f,i,p,r)}),o.dragListener=null}});return g},e.prototype.segDragStart=function(t,e){this.isDragging=!0,this.component.publiclyTrigger("eventDragStart",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},e.prototype.segDragStop=function(t,e){this.isDragging=!1,this.component.publiclyTrigger("eventDragStop",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},e.prototype.computeEventDropMutation=function(t,e,n){var i=new o.default;return i.setDateMutation(this.computeEventDateMutation(t,e)),i},e.prototype.computeEventDateMutation=function(t,e){var n,i,r=t.unzonedRange.getStart(),o=e.unzonedRange.getStart(),a=!1,l=!1,u=!1;return t.isAllDay!==e.isAllDay&&(a=!0,e.isAllDay?(u=!0,r.stripTime()):l=!0),n=this.component.diffDates(o,r),i=new s.default,i.clearEnd=a,i.forceTimed=l,i.forceAllDay=u,i.setDateDelta(n),i},e}(d.default);e.default=c},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(4),o=n(23),s=n(12),a=n(5),l=n(15),u=function(t){function e(e){var n=t.call(this,e)||this;return n.dragListener=n.buildDragListener(),n}return i.__extends(e,t),e.prototype.end=function(){this.dragListener.endInteraction()},e.prototype.getDelay=function(){var t=this.opt("selectLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},e.prototype.bindToEl=function(t){var e=this,n=this.component,i=this.dragListener;n.bindDateHandlerToEl(t,"mousedown",function(t){e.opt("selectable")&&!n.shouldIgnoreMouse()&&i.startInteraction(t,{distance:e.opt("selectMinDistance")})}),n.bindDateHandlerToEl(t,"touchstart",function(t){e.opt("selectable")&&!n.shouldIgnoreTouch()&&i.startInteraction(t,{delay:e.getDelay()})}),r.preventSelection(t)},e.prototype.buildDragListener=function(){var t,e=this,n=this.component;return new o.default(n,{scroll:this.opt("dragScroll"),interactionStart:function(){t=null},dragStart:function(t){e.view.unselect(t)},hitOver:function(i,o,s){var a,l;s&&(a=n.getSafeHitFootprint(s),l=n.getSafeHitFootprint(i),t=a&&l?e.computeSelection(a,l):null,t?n.renderSelectionFootprint(t):!1===t&&r.disableCursor())},hitOut:function(){t=null,n.unrenderSelection()},hitDone:function(){r.enableCursor()},interactionEnd:function(n,i){!i&&t&&e.view.reportSelection(t,n)}})},e.prototype.computeSelection=function(t,e){var n=this.computeSelectionFootprint(t,e);return!(n&&!this.isSelectionFootprintAllowed(n))&&n},e.prototype.computeSelectionFootprint=function(t,e){var n=[t.unzonedRange.startMs,t.unzonedRange.endMs,e.unzonedRange.startMs,e.unzonedRange.endMs];return n.sort(r.compareNumbers),new s.default(new a.default(n[0],n[3]),t.isAllDay)},e.prototype.isSelectionFootprintAllowed=function(t){return this.component.dateProfile.validUnzonedRange.containsRange(t.unzonedRange)&&this.view.calendar.constraints.isSelectionFootprintAllowed(t)},e}(l.default);e.default=u},function(t,e,n){function i(t){var e,n=[],i=[];for(e=0;e').appendTo(t),this.el.find(".fc-body > tr > td").append(t),this.timeGrid.headContainerEl=this.el.find(".fc-head-container"),this.timeGrid.setElement(e),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight())},e.prototype.unrenderSkeleton=function(){this.timeGrid.removeElement(),this.dayGrid&&this.dayGrid.removeElement(),this.scroller.destroy()},e.prototype.renderSkeletonHtml=function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'+(this.dayGrid?'

':"")+"
"},e.prototype.axisStyleAttr=function(){return null!=this.axisWidth?'style="width:'+this.axisWidth+'px"':""},e.prototype.getNowIndicatorUnit=function(){return this.timeGrid.getNowIndicatorUnit()},e.prototype.updateSize=function(e,n,i){var r,o,s;if(t.prototype.updateSize.call(this,e,n,i),this.axisWidth=u.matchCellWidths(this.el.find(".fc-axis")),!this.timeGrid.colEls)return void(n||(o=this.computeScrollerHeight(e),this.scroller.setHeight(o)));var a=this.el.find(".fc-row:not(.fc-scroller *)");this.timeGrid.bottomRuleEl.hide(),this.scroller.clear(),u.uncompensateScroll(a),this.dayGrid&&(this.dayGrid.removeSegPopover(),r=this.opt("eventLimit"),r&&"number"!=typeof r&&(r=5),r&&this.dayGrid.limitRows(r)),n||(o=this.computeScrollerHeight(e),this.scroller.setHeight(o),s=this.scroller.getScrollbarWidths(),(s.left||s.right)&&(u.compensateScroll(a,s),o=this.computeScrollerHeight(e),this.scroller.setHeight(o)),this.scroller.lockOverflow(s),this.timeGrid.getTotalSlatHeight()"+e.buildGotoAnchorHtml({date:i,type:"week",forceOff:this.colCnt>1},u.htmlEscape(t))+""):'"},renderBgIntroHtml:function(){var t=this.view;return'"},renderIntroHtml:function(){return'"}},o={renderBgIntroHtml:function(){var t=this.view;return'"+t.getAllDayHtml()+""},renderIntroHtml:function(){return'"}}},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(0),s=n(4),a=n(40),l=n(56),u=n(60),d=n(55),c=n(53),p=n(5),h=n(12),f=n(246),g=n(247),v=n(248),y=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}],m=function(t){function e(e){var n=t.call(this,e)||this;return n.processOptions(),n}return i.__extends(e,t),e.prototype.componentFootprintToSegs=function(t){var e,n=this.sliceRangeByTimes(t.unzonedRange);for(e=0;e=0;e--)if(n=o.duration(y[e]),i=s.divideDurationByDuration(n,t),s.isInt(i)&&i>1)return n;return o.duration(t)},e.prototype.renderDates=function(t){this.dateProfile=t,this.updateDayTable(),this.renderSlats(),this.renderColumns()},e.prototype.unrenderDates=function(){this.unrenderColumns()},e.prototype.renderSkeleton=function(){var t=this.view.calendar.theme;this.el.html('
'),this.bottomRuleEl=this.el.find("hr")},e.prototype.renderSlats=function(){var t=this.view.calendar.theme;this.slatContainerEl=this.el.find("> .fc-slats").html(''+this.renderSlatRowHtml()+"
"),this.slatEls=this.slatContainerEl.find("tr"),this.slatCoordCache=new c.default({els:this.slatEls,isVertical:!0})},e.prototype.renderSlatRowHtml=function(){for(var t,e,n,i=this.view,r=i.calendar,a=r.theme,l=this.isRTL,u=this.dateProfile,d="",c=o.duration(+u.minTime),p=o.duration(0);c"+(e?""+s.htmlEscape(t.format(this.labelFormat))+"":"")+"",d+='"+(l?"":n)+''+(l?n:"")+"",c.add(this.slotDuration),p.add(this.slotDuration);return d},e.prototype.renderColumns=function(){var t=this.dateProfile,e=this.view.calendar.theme;this.dayRanges=this.dayDates.map(function(e){return new p.default(e.clone().add(t.minTime),e.clone().add(t.maxTime))}),this.headContainerEl&&this.headContainerEl.html(this.renderHeadHtml()),this.el.find("> .fc-bg").html(''+this.renderBgTrHtml(0)+"
"),this.colEls=this.el.find(".fc-day, .fc-disabled-day"),this.colCoordCache=new c.default({els:this.colEls,isHorizontal:!0}),this.renderContentSkeleton()},e.prototype.unrenderColumns=function(){this.unrenderContentSkeleton()},e.prototype.renderContentSkeleton=function(){var t,e,n="";for(t=0;t
';e=this.contentSkeletonEl=r('
'+n+"
"),this.colContainerEls=e.find(".fc-content-col"),this.helperContainerEls=e.find(".fc-helper-container"),this.fgContainerEls=e.find(".fc-event-container:not(.fc-helper-container)"),this.bgContainerEls=e.find(".fc-bgevent-container"),this.highlightContainerEls=e.find(".fc-highlight-container"),this.businessContainerEls=e.find(".fc-business-container"),this.bookendCells(e.find("tr")),this.el.append(e)},e.prototype.unrenderContentSkeleton=function(){this.contentSkeletonEl&&(this.contentSkeletonEl.remove(),this.contentSkeletonEl=null,this.colContainerEls=null,this.helperContainerEls=null,this.fgContainerEls=null,this.bgContainerEls=null,this.highlightContainerEls=null,this.businessContainerEls=null)},e.prototype.groupSegsByCol=function(t){var e,n=[];for(e=0;e
').css("top",i).appendTo(this.colContainerEls.eq(n[e].col))[0]);n.length>0&&o.push(r('
').css("top",i).appendTo(this.el.find(".fc-content-skeleton"))[0]),this.nowIndicatorEls=r(o)}},e.prototype.unrenderNowIndicator=function(){this.nowIndicatorEls&&(this.nowIndicatorEls.remove(),this.nowIndicatorEls=null)},e.prototype.updateSize=function(e,n,i){t.prototype.updateSize.call(this,e,n,i),this.slatCoordCache.build(),i&&this.updateSegVerticals([].concat(this.eventRenderer.getSegs(),this.businessSegs||[]))},e.prototype.getTotalSlatHeight=function(){return this.slatContainerEl.outerHeight()},e.prototype.computeDateTop=function(t,e){return this.computeTimeTop(o.duration(t-e.clone().stripTime()))},e.prototype.computeTimeTop=function(t){var e,n,i=this.slatEls.length,r=this.dateProfile,o=(t-r.minTime)/this.slotDuration;return o=Math.max(0,o),o=Math.min(i,o),e=Math.floor(o),e=Math.min(e,i-1),n=o-e,this.slatCoordCache.getTopPosition(e)+this.slatCoordCache.getHeight(e)*n},e.prototype.updateSegVerticals=function(t){this.computeSegVerticals(t),this.assignSegVerticals(t)},e.prototype.computeSegVerticals=function(t){var e,n,i,r=this.opt("agendaEventMinHeight");for(e=0;e
'+o.htmlEscape(this.opt("noEventsMessage"))+"
")},e.prototype.renderSegList=function(t){var e,n,i,o=this.groupSegsByDay(t),s=r('
'),a=s.find("tbody");for(e=0;e'+(e?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-main"},o.htmlEscape(t.format(e))):"")+(n?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-alt"},o.htmlEscape(t.format(n))):"")+""},e}(a.default);e.default=c,c.prototype.eventRendererClass=u.default,c.prototype.eventPointingClass=d.default},,,,,,function(t,e,n){var i=n(3),r=n(16),o=n(4),s=n(220);n(10),n(47),n(256),n(257),n(260),n(261),n(262),n(263),i.fullCalendar=r,i.fn.fullCalendar=function(t){var e=Array.prototype.slice.call(arguments,1),n=this;return this.each(function(r,a){var l,u=i(a),d=u.data("fullCalendar");"string"==typeof t?"getCalendar"===t?r||(n=d):"destroy"===t?d&&(d.destroy(),u.removeData("fullCalendar")):d?i.isFunction(d[t])?(l=d[t].apply(d,e),r||(n=l),"destroy"===t&&u.removeData("fullCalendar")):o.warn("'"+t+"' is an unknown FullCalendar method."):o.warn("Attempting to call a FullCalendar method on an element with no calendar."):d||(d=new s.default(u,t),u.data("fullCalendar",d),d.render())}),n},t.exports=r},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(48),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.setElement=function(t){this.el=t,this.bindGlobalHandlers(),this.renderSkeleton(),this.set("isInDom",!0)},e.prototype.removeElement=function(){this.unset("isInDom"),this.unrenderSkeleton(),this.unbindGlobalHandlers(),this.el.remove()},e.prototype.bindGlobalHandlers=function(){},e.prototype.unbindGlobalHandlers=function(){},e.prototype.renderSkeleton=function(){},e.prototype.unrenderSkeleton=function(){},e}(r.default);e.default=o},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t){this.items=t||[]}return t.prototype.proxyCall=function(t){for(var e=[],n=1;n"),e.append(this.renderSection("left")).append(this.renderSection("right")).append(this.renderSection("center")).append('
')):this.removeElement()},t.prototype.removeElement=function(){this.el&&(this.el.remove(),this.el=null)},t.prototype.renderSection=function(t){var e=this,n=this.calendar,o=n.theme,s=n.optionsManager,a=n.viewSpecManager,l=i('
'),u=this.toolbarOptions.layout[t],d=s.get("customButtons")||{},c=s.overrides.buttonText||{},p=s.get("buttonText")||{};return u&&i.each(u.split(" "),function(t,s){var u,h=i(),f=!0;i.each(s.split(","),function(t,s){var l,u,g,v,y,m,b,w,D;"title"===s?(h=h.add(i("

 

")),f=!1):((l=d[s])?(g=function(t){l.click&&l.click.call(w[0],t)},(v=o.getCustomButtonIconClass(l))||(v=o.getIconClass(s))||(y=l.text)):(u=a.getViewSpec(s))?(e.viewsWithButtons.push(s),g=function(){n.changeView(s)},(y=u.buttonTextOverride)||(v=o.getIconClass(s))||(y=u.buttonTextDefault)):n[s]&&(g=function(){n[s]()},(y=c[s])||(v=o.getIconClass(s))||(y=p[s])),g&&(b=["fc-"+s+"-button",o.getClass("button"),o.getClass("stateDefault")],y?(m=r.htmlEscape(y),D=""):v&&(m="",D=' aria-label="'+s+'"'),w=i('").click(function(t){w.hasClass(o.getClass("stateDisabled"))||(g(t),(w.hasClass(o.getClass("stateActive"))||w.hasClass(o.getClass("stateDisabled")))&&w.removeClass(o.getClass("stateHover")))}).mousedown(function(){w.not("."+o.getClass("stateActive")).not("."+o.getClass("stateDisabled")).addClass(o.getClass("stateDown"))}).mouseup(function(){w.removeClass(o.getClass("stateDown"))}).hover(function(){w.not("."+o.getClass("stateActive")).not("."+o.getClass("stateDisabled")).addClass(o.getClass("stateHover"))},function(){w.removeClass(o.getClass("stateHover")).removeClass(o.getClass("stateDown"))}),h=h.add(w)))}),f&&h.first().addClass(o.getClass("cornerLeft")).end().last().addClass(o.getClass("cornerRight")).end(),h.length>1?(u=i("
"),f&&u.addClass(o.getClass("buttonGroup")),u.append(h),l.append(u)):l.append(h)}),l},t.prototype.updateTitle=function(t){this.el&&this.el.find("h2").text(t)},t.prototype.activateButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").addClass(this.calendar.theme.getClass("stateActive"))},t.prototype.deactivateButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").removeClass(this.calendar.theme.getClass("stateActive"))},t.prototype.disableButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").prop("disabled",!0).addClass(this.calendar.theme.getClass("stateDisabled"))},t.prototype.enableButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").prop("disabled",!1).removeClass(this.calendar.theme.getClass("stateDisabled"))},t.prototype.getViewsWithButtons=function(){return this.viewsWithButtons},t}();e.default=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(4),s=n(32),a=n(31),l=n(48),u=function(t){function e(e,n){var i=t.call(this)||this;return i._calendar=e,i.overrides=r.extend({},n),i.dynamicOverrides={},i.compute(),i}return i.__extends(e,t),e.prototype.add=function(t){var e,n=0;this.recordOverrides(t);for(e in t)n++;if(1===n){if("height"===e||"contentHeight"===e||"aspectRatio"===e)return void this._calendar.updateViewSize(!0);if("defaultDate"===e)return;if("businessHours"===e)return;if(/^(event|select)(Overlap|Constraint|Allow)$/.test(e))return;if("timezone"===e)return void this._calendar.view.flash("initialEvents")}this._calendar.renderHeader(),this._calendar.renderFooter(),this._calendar.viewsByType={},this._calendar.reinitView()},e.prototype.compute=function(){var t,e,n,i,r;t=o.firstDefined(this.dynamicOverrides.locale,this.overrides.locale),e=a.localeOptionHash[t],e||(t=s.globalDefaults.locale,e=a.localeOptionHash[t]||{}),n=o.firstDefined(this.dynamicOverrides.isRTL,this.overrides.isRTL,e.isRTL,s.globalDefaults.isRTL),i=n?s.rtlDefaults:{},this.dirDefaults=i,this.localeDefaults=e,r=s.mergeOptions([s.globalDefaults,i,e,this.overrides,this.dynamicOverrides]),a.populateInstanceComputableOptions(r),this.reset(r)},e.prototype.recordOverrides=function(t){var e;for(e in t)this.dynamicOverrides[e]=t[e];this._calendar.viewSpecManager.clearCache(),this.compute()},e}(l.default);e.default=u},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(0),r=n(3),o=n(22),s=n(4),a=n(32),l=n(31),u=function(){function t(t,e){this.optionsManager=t,this._calendar=e,this.clearCache()}return t.prototype.clearCache=function(){this.viewSpecCache={}},t.prototype.getViewSpec=function(t){var e=this.viewSpecCache;return e[t]||(e[t]=this.buildViewSpec(t))},t.prototype.getUnitViewSpec=function(t){var e,n,i;if(-1!==r.inArray(t,s.unitsDesc))for(e=this._calendar.header.getViewsWithButtons(),r.each(o.viewHash,function(t){e.push(t)}),n=0;ne.top&&t.top
'+(n?'
'+u.htmlEscape(n)+"
":"")+(d.title?'
'+u.htmlEscape(d.title)+"
":"")+'
'+(h?'
':"")+""},e.prototype.updateFgSegCoords=function(t){this.timeGrid.computeSegVerticals(t),this.computeFgSegHorizontals(t),this.timeGrid.assignSegVerticals(t),this.assignFgSegHorizontals(t)},e.prototype.computeFgSegHorizontals=function(t){var e,n,s;if(this.sortEventSegs(t),e=i(t),r(e),n=e[0]){for(s=0;s').addClass(e.className||"").css({top:0,left:0}).append(e.content).appendTo(e.parentEl),this.el.on("click",".fc-close",function(){t.hide()}),e.autoHide&&this.listenTo(i(document),"mousedown",this.documentMousedown)},t.prototype.documentMousedown=function(t){this.el&&!i(t.target).closest(this.el).length&&this.hide()},t.prototype.removeElement=function(){this.hide(),this.el&&(this.el.remove(),this.el=null),this.stopListeningTo(i(document),"mousedown")},t.prototype.position=function(){var t,e,n,o,s,a=this.options,l=this.el.offsetParent().offset(),u=this.el.outerWidth(),d=this.el.outerHeight(),c=i(window),p=r.getScrollParent(this.el);o=a.top||0,s=void 0!==a.left?a.left:void 0!==a.right?a.right-u:0,p.is(window)||p.is(document)?(p=c,t=0,e=0):(n=p.offset(),t=n.top,e=n.left),t+=c.scrollTop(),e+=c.scrollLeft(),!1!==a.viewportConstrain&&(o=Math.min(o,t+p.outerHeight()-d-this.margin),o=Math.max(o,t+this.margin),s=Math.min(s,e+p.outerWidth()-u-this.margin),s=Math.max(s,e+this.margin)),this.el.css({top:o-l.top,left:s-l.left})},t.prototype.trigger=function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))},t}();e.default=s,o.default.mixInto(s)},function(t,e,n){function i(t,e){var n,i;for(n=0;n=t.leftCol)return!0;return!1}function r(t,e){return t.leftCol-e.leftCol}Object.defineProperty(e,"__esModule",{value:!0});var o=n(2),s=n(3),a=n(4),l=n(42),u=function(t){function e(e,n){var i=t.call(this,e,n)||this;return i.dayGrid=e,i}return o.__extends(e,t),e.prototype.renderBgRanges=function(e){e=s.grep(e,function(t){return t.eventDef.isAllDay()}),t.prototype.renderBgRanges.call(this,e)},e.prototype.renderFgSegs=function(t){var e=this.rowStructs=this.renderSegRows(t);this.dayGrid.rowEls.each(function(t,n){s(n).find(".fc-content-skeleton > table").append(e[t].tbodyEl)})},e.prototype.unrenderFgSegs=function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},e.prototype.renderSegRows=function(t){var e,n,i=[];for(e=this.groupSegRows(t),n=0;n"),a.append(d)),v[i][o]=d,y[i][o]=d,o++}var i,r,o,a,l,u,d,c=this.dayGrid.colCnt,p=this.buildSegLevels(e),h=Math.max(1,p.length),f=s(""),g=[],v=[],y=[];for(i=0;i"),g.push([]),v.push([]),y.push([]),r)for(l=0;l').append(u.el),u.leftCol!==u.rightCol?d.attr("colspan",u.rightCol-u.leftCol+1):y[i][o]=d;o<=u.rightCol;)v[i][o]=d,g[i][o]=u,o++;a.append(d)}n(c),this.dayGrid.bookendCells(a),f.append(a)}return{row:t,tbodyEl:f,cellMatrix:v,segMatrix:g,segLevels:p,segs:e}},e.prototype.buildSegLevels=function(t){var e,n,o,s=[];for(this.sortEventSegs(t),e=0;e'+a.htmlEscape(n)+""),i=''+(a.htmlEscape(o.title||"")||" ")+"",'
'+(this.dayGrid.isRTL?i+" "+h:h+" "+i)+"
"+(u?'
':"")+(d?'
':"")+""},e}(l.default);e.default=u},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(58),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.renderSegs=function(t,e){var n,i=[];return n=this.eventRenderer.renderSegRows(t),this.component.rowEls.each(function(t,o){var s,a,l=r(o),u=r('
');e&&e.row===t?a=e.el.position().top:(s=l.find(".fc-content-skeleton tbody"),s.length||(s=l.find(".fc-content-skeleton table")),a=s.position().top),u.css("top",a).find("table").append(n[t].tbodyEl),l.append(u),i.push(u[0])}),r(i)},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(57),s=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.fillSegTag="td",e}return i.__extends(e,t),e.prototype.attachSegEls=function(t,e){var n,i,r,o=[];for(n=0;n
'),o=i.find("tr"),a>0&&o.append(''),o.append(e.el.attr("colspan",l-a)),l'),this.component.bookendCells(o),i},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(228),o=n(5),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.buildRenderRange=function(e,n,i){var r,s=t.prototype.buildRenderRange.call(this,e,n,i),a=this.msToUtcMoment(s.startMs,i),l=this.msToUtcMoment(s.endMs,i);return this.opt("fixedWeekCount")&&(r=Math.ceil(l.diff(a,"weeks",!0)),l.add(6-r,"weeks")),new o.default(a,l)},e}(r.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(4),o=n(42),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.renderFgSegs=function(t){t.length?this.component.renderSegList(t):this.component.renderEmptyMessage()},e.prototype.fgSegHtml=function(t){var e,n=this.view,i=n.calendar,o=i.theme,s=t.footprint,a=s.eventDef,l=s.componentFootprint,u=a.url,d=["fc-list-item"].concat(this.getClasses(a)),c=this.getBgColor(a);return e=l.isAllDay?n.getAllDayHtml():n.isMultiDayRange(l.unzonedRange)?t.isStart||t.isEnd?r.htmlEscape(this._getTimeText(i.msToMoment(t.startMs),i.msToMoment(t.endMs),l.isAllDay)):n.getAllDayHtml():r.htmlEscape(this.getTimeText(s)),u&&d.push("fc-has-url"),''+(this.displayEventTime?''+(e||"")+"":"")+'"+r.htmlEscape(a.title||"")+""},e.prototype.computeEventTimeFormat=function(){return this.opt("mediumTimeFormat")},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(3),o=n(59),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e.prototype.handleClick=function(e,n){var i;t.prototype.handleClick.call(this,e,n),r(n.target).closest("a[href]").length||(i=e.footprint.eventDef.url)&&!n.isDefaultPrevented()&&(window.location.href=i)},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(38),r=n(52),o=n(215),s=n(216);i.default.registerClass(r.default),i.default.registerClass(o.default),i.default.registerClass(s.default)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(51),r=n(213),o=n(214),s=n(258),a=n(259);i.defineThemeSystem("standard",r.default),i.defineThemeSystem("jquery-ui",o.default),i.defineThemeSystem("bootstrap3",s.default),i.defineThemeSystem("bootstrap4",a.default)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(19),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e}(r.default);e.default=o,o.prototype.classes={widget:"fc-bootstrap3",tableGrid:"table-bordered",tableList:"table",tableListHeading:"active",buttonGroup:"btn-group",button:"btn btn-default",stateActive:"active",stateDisabled:"disabled",today:"alert alert-info",popover:"panel panel-default",popoverHeader:"panel-heading",popoverContent:"panel-body",headerRow:"panel-default",dayRow:"panel-default",listView:"panel panel-default"},o.prototype.baseIconClass="glyphicon",o.prototype.iconClasses={close:"glyphicon-remove",prev:"glyphicon-chevron-left",next:"glyphicon-chevron-right",prevYear:"glyphicon-backward",nextYear:"glyphicon-forward"},o.prototype.iconOverrideOption="bootstrapGlyphicons",o.prototype.iconOverrideCustomButtonOption="bootstrapGlyphicon",o.prototype.iconOverridePrefix="glyphicon-"},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),r=n(19),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return i.__extends(e,t),e}(r.default);e.default=o,o.prototype.classes={widget:"fc-bootstrap4",tableGrid:"table-bordered",tableList:"table",tableListHeading:"table-active",buttonGroup:"btn-group",button:"btn btn-primary",stateActive:"active",stateDisabled:"disabled",today:"alert alert-info",popover:"card card-primary",popoverHeader:"card-header",popoverContent:"card-body",headerRow:"table-bordered",dayRow:"table-bordered",listView:"card card-primary"},o.prototype.baseIconClass="fa",o.prototype.iconClasses={close:"fa-times",prev:"fa-chevron-left",next:"fa-chevron-right",prevYear:"fa-angle-double-left",nextYear:"fa-angle-double-right"},o.prototype.iconOverrideOption="bootstrapFontAwesome",o.prototype.iconOverrideCustomButtonOption="bootstrapFontAwesome",o.prototype.iconOverridePrefix="fa-"},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(22),r=n(62),o=n(229);i.defineView("basic",{class:r.default}),i.defineView("basicDay",{type:"basic",duration:{days:1}}),i.defineView("basicWeek",{type:"basic",duration:{weeks:1}}),i.defineView("month",{class:o.default,duration:{months:1},defaults:{fixedWeekCount:!0}})},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(22),r=n(226);i.defineView("agenda",{class:r.default,defaults:{allDaySlot:!0,slotDuration:"00:30:00",slotEventOverlap:!0}}),i.defineView("agendaDay",{type:"agenda",duration:{days:1}}),i.defineView("agendaWeek",{type:"agenda",duration:{weeks:1}})},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var i=n(22),r=n(230);i.defineView("list",{class:r.default,buttonTextKey:"list",defaults:{buttonText:"list",listDayFormat:"LL",noEventsMessage:"No events to display"}}),i.defineView("listDay",{type:"list",duration:{days:1},defaults:{listDayFormat:"dddd"}}),i.defineView("listWeek",{type:"list",duration:{weeks:1},defaults:{listDayFormat:"dddd",listDayAltFormat:"LL"}}),i.defineView("listMonth",{type:"list",duration:{month:1},defaults:{listDayAltFormat:"dddd"}}),i.defineView("listYear",{type:"list",duration:{year:1},defaults:{listDayAltFormat:"dddd"}})},function(t,e){Object.defineProperty(e,"__esModule",{value:!0})}])}); \ No newline at end of file diff --git a/public/lib/fc/fullcalendar.print.css b/public/lib/fc/fullcalendar.print.css new file mode 100644 index 0000000000..fb858cd790 --- /dev/null +++ b/public/lib/fc/fullcalendar.print.css @@ -0,0 +1,176 @@ +/*! + * FullCalendar v3.9.0 + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */ +/*! + * FullCalendar v3.9.0 Print Stylesheet + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */ +/* + * Include this stylesheet on your page to get a more printer-friendly calendar. + * When including this stylesheet, use the media='print' attribute of the tag. + * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css. + */ +.fc { + max-width: 100% !important; } + +/* Global Event Restyling +--------------------------------------------------------------------------------------------------*/ +.fc-event { + background: #fff !important; + color: #000 !important; + page-break-inside: avoid; } + +.fc-event .fc-resizer { + display: none; } + +/* Table & Day-Row Restyling +--------------------------------------------------------------------------------------------------*/ +.fc th, +.fc td, +.fc hr, +.fc thead, +.fc tbody, +.fc-row { + border-color: #ccc !important; + background: #fff !important; } + +/* kill the overlaid, absolutely-positioned components */ +/* common... */ +.fc-bg, +.fc-bgevent-skeleton, +.fc-highlight-skeleton, +.fc-helper-skeleton, +.fc-bgevent-container, +.fc-business-container, +.fc-highlight-container, +.fc-helper-container { + display: none; } + +/* don't force a min-height on rows (for DayGrid) */ +.fc tbody .fc-row { + height: auto !important; + /* undo height that JS set in distributeHeight */ + min-height: 0 !important; + /* undo the min-height from each view's specific stylesheet */ } + +.fc tbody .fc-row .fc-content-skeleton { + position: static; + /* undo .fc-rigid */ + padding-bottom: 0 !important; + /* use a more border-friendly method for this... */ } + +.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { + /* only works in newer browsers */ + padding-bottom: 1em; + /* ...gives space within the skeleton. also ensures min height in a way */ } + +.fc tbody .fc-row .fc-content-skeleton table { + /* provides a min-height for the row, but only effective for IE, which exaggerates this value, + making it look more like 3em. for other browers, it will already be this tall */ + height: 1em; } + +/* Undo month-view event limiting. Display all events and hide the "more" links +--------------------------------------------------------------------------------------------------*/ +.fc-more-cell, +.fc-more { + display: none !important; } + +.fc tr.fc-limited { + display: table-row !important; } + +.fc td.fc-limited { + display: table-cell !important; } + +.fc-popover { + display: none; + /* never display the "more.." popover in print mode */ } + +/* TimeGrid Restyling +--------------------------------------------------------------------------------------------------*/ +/* undo the min-height 100% trick used to fill the container's height */ +.fc-time-grid { + min-height: 0 !important; } + +/* don't display the side axis at all ("all-day" and time cells) */ +.fc-agenda-view .fc-axis { + display: none; } + +/* don't display the horizontal lines */ +.fc-slats, +.fc-time-grid hr { + /* this hr is used when height is underused and needs to be filled */ + display: none !important; + /* important overrides inline declaration */ } + +/* let the container that holds the events be naturally positioned and create real height */ +.fc-time-grid .fc-content-skeleton { + position: static; } + +/* in case there are no events, we still want some height */ +.fc-time-grid .fc-content-skeleton table { + height: 4em; } + +/* kill the horizontal spacing made by the event container. event margins will be done below */ +.fc-time-grid .fc-event-container { + margin: 0 !important; } + +/* TimeGrid *Event* Restyling +--------------------------------------------------------------------------------------------------*/ +/* naturally position events, vertically stacking them */ +.fc-time-grid .fc-event { + position: static !important; + margin: 3px 2px !important; } + +/* for events that continue to a future day, give the bottom border back */ +.fc-time-grid .fc-event.fc-not-end { + border-bottom-width: 1px !important; } + +/* indicate the event continues via "..." text */ +.fc-time-grid .fc-event.fc-not-end:after { + content: "..."; } + +/* for events that are continuations from previous days, give the top border back */ +.fc-time-grid .fc-event.fc-not-start { + border-top-width: 1px !important; } + +/* indicate the event is a continuation via "..." text */ +.fc-time-grid .fc-event.fc-not-start:before { + content: "..."; } + +/* time */ +/* undo a previous declaration and let the time text span to a second line */ +.fc-time-grid .fc-event .fc-time { + white-space: normal !important; } + +/* hide the the time that is normally displayed... */ +.fc-time-grid .fc-event .fc-time span { + display: none; } + +/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */ +.fc-time-grid .fc-event .fc-time:after { + content: attr(data-full); } + +/* Vertical Scroller & Containers +--------------------------------------------------------------------------------------------------*/ +/* kill the scrollbars and allow natural height */ +.fc-scroller, +.fc-day-grid-container, +.fc-time-grid-container { + /* */ + overflow: visible !important; + height: auto !important; } + +/* kill the horizontal border/padding used to compensate for scrollbars */ +.fc-row { + border: 0 !important; + margin: 0 !important; } + +/* Button Controls +--------------------------------------------------------------------------------------------------*/ +.fc-button-group, +.fc button { + display: none; + /* don't display any button-related controls */ } diff --git a/public/lib/fc/fullcalendar.print.min.css b/public/lib/fc/fullcalendar.print.min.css new file mode 100644 index 0000000000..59a405c0a4 --- /dev/null +++ b/public/lib/fc/fullcalendar.print.min.css @@ -0,0 +1,9 @@ +/*! + * FullCalendar v3.9.0 + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + *//*! + * FullCalendar v3.9.0 Print Stylesheet + * Docs & License: https://fullcalendar.io/ + * (c) 2018 Adam Shaw + */.fc-bg,.fc-bgevent-container,.fc-bgevent-skeleton,.fc-business-container,.fc-event .fc-resizer,.fc-helper-container,.fc-helper-skeleton,.fc-highlight-container,.fc-highlight-skeleton{display:none}.fc tbody .fc-row,.fc-time-grid{min-height:0!important}.fc-time-grid .fc-event.fc-not-end:after,.fc-time-grid .fc-event.fc-not-start:before{content:"..."}.fc{max-width:100%!important}.fc-event{background:#fff!important;color:#000!important;page-break-inside:avoid}.fc hr,.fc tbody,.fc td,.fc th,.fc thead,.fc-row{border-color:#ccc!important;background:#fff!important}.fc tbody .fc-row{height:auto!important}.fc tbody .fc-row .fc-content-skeleton{position:static;padding-bottom:0!important}.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td{padding-bottom:1em}.fc tbody .fc-row .fc-content-skeleton table{height:1em}.fc-more,.fc-more-cell{display:none!important}.fc tr.fc-limited{display:table-row!important}.fc td.fc-limited{display:table-cell!important}.fc-agenda-view .fc-axis,.fc-popover{display:none}.fc-slats,.fc-time-grid hr{display:none!important}.fc button,.fc-button-group,.fc-time-grid .fc-event .fc-time span{display:none}.fc-time-grid .fc-content-skeleton{position:static}.fc-time-grid .fc-content-skeleton table{height:4em}.fc-time-grid .fc-event-container{margin:0!important}.fc-time-grid .fc-event{position:static!important;margin:3px 2px!important}.fc-time-grid .fc-event.fc-not-end{border-bottom-width:1px!important}.fc-time-grid .fc-event.fc-not-start{border-top-width:1px!important}.fc-time-grid .fc-event .fc-time{white-space:normal!important}.fc-time-grid .fc-event .fc-time:after{content:attr(data-full)}.fc-day-grid-container,.fc-scroller,.fc-time-grid-container{overflow:visible!important;height:auto!important}.fc-row{border:0!important;margin:0!important} \ No newline at end of file diff --git a/readme.md b/readme.md index ca966ebf2b..f6cb936fa2 100644 --- a/readme.md +++ b/readme.md @@ -2,14 +2,12 @@

Firefly III

- Packagist License - - Patreon - + + Donate

@@ -44,38 +42,38 @@ Personal financial management is pretty difficult, and everybody has their own a By keeping track of your expenses and your income you can budget accordingly and save money. Stop living from paycheck to paycheck but give yourself the financial wiggle room you need. -You can read more about this in the [official documentation](http://firefly-iii.readthedocs.io/en/latest/index.html). +You can read more about this in the [official documentation](https://firefly-iii.readthedocs.io/en/latest/index.html). ### Features Most importantly... * Firefly III runs on your own server, so you are fully in control of your data. It will not contact other sites or servers. -* You can import from over 2500 financial providers, in 55 countries when you enable the [Spectre API](http://firefly-iii.readthedocs.io/en/latest/import/spectre.html). +* You can import from over 2500 financial providers, in 55 countries when you enable the [Spectre API](https://firefly-iii.readthedocs.io/en/latest/import/spectre.html). * You can import from [bunq](https://www.bunq.com/). * You can import CSV files from practically any bank. -* Firefly III features an JSON REST API. +* Firefly III features an [JSON REST API](https://firefly-iii.readthedocs.io/en/latest/api/start.html). * If you feel you’re missing something you can just ask me and I’ll add it! But actually, it features: -* [A double-entry bookkeeping system](http://firefly-iii.readthedocs.io/en/latest/concepts/transactions.html) -* You can store, edit and remove [withdrawals, deposits and transfers](http://firefly-iii.readthedocs.io/en/latest/concepts/transactions.html). This allows you full financial management +* [A double-entry bookkeeping system](https://firefly-iii.readthedocs.io/en/latest/concepts/transactions.html) +* You can store, edit and remove [withdrawals, deposits and transfers](https://firefly-iii.readthedocs.io/en/latest/concepts/transactions.html). This allows you full financial management * You can manage different types of accounts - * [Asset](http://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html) accounts - * Shared [asset accounts](http://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html) ([household accounts](http://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html)) + * [Asset](https://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html) accounts + * Shared [asset accounts](https://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html) ([household accounts](https://firefly-iii.readthedocs.io/en/latest/concepts/accounts.html)) * Saving accounts * Credit cards -* It's possible to create, change and manage money using [budgets](http://firefly-iii.readthedocs.io/en/latest/concepts/budgets.html) -* Organize transactions using [categories](http://firefly-iii.readthedocs.io/en/latest/concepts/categories.html) -* Save towards a goal using [piggy banks](http://firefly-iii.readthedocs.io/en/latest/advanced/piggies.html) -* Predict and anticipate [bills](http://firefly-iii.readthedocs.io/en/latest/advanced/bills.html) -* View income / expense [reports](http://firefly-iii.readthedocs.io/en/latest/advanced/reports.html) -* [Rule based](http://firefly-iii.readthedocs.io/en/latest/advanced/rules.html) transaction handling with the ability to create your own rules. -* The ability to [export data](http://firefly-iii.readthedocs.io/en/latest/import/export.html) so you can move to another system. -* The ability to [import data](http://firefly-iii.readthedocs.io/en/latest/import/csv.html) so you can move _from_ another system. -* Organize expenses using [tags](http://firefly-iii.readthedocs.io/en/latest/concepts/tags.html) +* It's possible to create, change and manage money using [budgets](https://firefly-iii.readthedocs.io/en/latest/concepts/budgets.html) +* Organize transactions using [categories](https://firefly-iii.readthedocs.io/en/latest/concepts/categories.html) +* Save towards a goal using [piggy banks](https://firefly-iii.readthedocs.io/en/latest/advanced/piggies.html) +* Predict and anticipate [bills](https://firefly-iii.readthedocs.io/en/latest/advanced/bills.html) +* View income / expense [reports](https://firefly-iii.readthedocs.io/en/latest/advanced/reports.html) +* [Rule based](https://firefly-iii.readthedocs.io/en/latest/advanced/rules.html) transaction handling with the ability to create your own rules. +* The ability to [export data](https://firefly-iii.readthedocs.io/en/latest/import/export.html) so you can move to another system. +* The ability to [import data](https://firefly-iii.readthedocs.io/en/latest/import/csv.html) so you can move _from_ another system. +* Organize expenses using [tags](https://firefly-iii.readthedocs.io/en/latest/concepts/tags.html) * 2 factor authentication for extra security 🔒 -* Supports any currency you want, including [crypto currencies](http://firefly-iii.readthedocs.io/en/latest/concepts/currencies.html) such as ₿itcoin and Ξthereum +* Supports any currency you want, including [crypto currencies](https://firefly-iii.readthedocs.io/en/latest/concepts/currencies.html) such as ₿itcoin and Ξthereum * Lots of help text in case you don’t get it * Translations into 10(!) languages, proudly powered by Crowdin @@ -94,13 +92,14 @@ This application is for people who want to track their finances, keep an eye on ## Get started There are many ways to run Firefly III 1. There is a [demo site](https://demo.firefly-iii.org) with an example financial administration already present. -2. You can [install it on your server](http://firefly-iii.readthedocs.io/en/latest/installation/server.html). -3. You can [run it using Docker](http://firefly-iii.readthedocs.io/en/latest/installation/docker.html). +2. You can [install it on your server](https://firefly-iii.readthedocs.io/en/latest/installation/server.html). +3. You can [run it using Docker](https://firefly-iii.readthedocs.io/en/latest/installation/docker.html). 4. You can [deploy to Heroku](https://heroku.com/deploy?template=https://github.com/firefly-iii/firefly-iii/tree/master) 5. You can [deploy to Sandstorm.io](https://apps.sandstorm.io/app/uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70) -6. You can [install it using Softaculous](https://softaculous.com/). These guys even have made [another demo site](http://www.softaculous.com/softaculous/apps/others/Firefly_III)! +6. You can [install it using Softaculous](https://softaculous.com/). These guys even have made [another demo site](https://www.softaculous.com/softaculous/apps/others/Firefly_III)! 7. You can [install it using AMPPS](https://www.ampps.com/) -5. *Even more options are on the way!* +8. You can [install it with YunoHost](https://install-app.yunohost.org/?app=firefly-iii). +9. *Even more options are on the way!* ### Update your instance Make sure you check for updates regularly. Your Firefly III instance will ask you to do this. Upgrade instructions can be found with the installation instructions. @@ -110,7 +109,7 @@ Your help is always welcome! Feel free to open issues, ask questions, talk about Of course there are some [contributing guidelines](https://github.com/firefly-iii/firefly-iii/blob/master/.github/contributing.md) and a [code of conduct](https://github.com/firefly-iii/firefly-iii/blob/master/.github/code_of_conduct.md), which I invite you to check out. -I can always use your help [squashing bugs](http://firefly-iii.readthedocs.io/en/latest/support/contribute.html#bugs), thinking about [new features](http://firefly-iii.readthedocs.io/en/latest/support/contribute.html#feature-requests) or [translating Firefly III](http://firefly-iii.readthedocs.io/en/latest/support/contribute.html#translations) into other languages. +I can always use your help [squashing bugs](https://firefly-iii.readthedocs.io/en/latest/support/contribute.html#bugs), thinking about [new features](https://firefly-iii.readthedocs.io/en/latest/support/contribute.html#feature-requests) or [translating Firefly III](https://firefly-iii.readthedocs.io/en/latest/support/contribute.html#translations) into other languages. For all other contributions, see below. @@ -126,7 +125,7 @@ Over time, [many people have contributed to Firefly III](https://github.com/fire ## Other stuff ### Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see [the tags](https://github.com/firefly-iii/firefly-iii/tags) on this repository. +We use [SemVer](https://semver.org/) for versioning. For the versions available, see [the tags](https://github.com/firefly-iii/firefly-iii/tags) on this repository. ### License This work [is licensed](https://github.com/firefly-iii/firefly-iii/blob/master/LICENSE) under the [GPL v3](https://www.gnu.org/licenses/gpl.html). @@ -142,4 +141,4 @@ If you are looking for alternatives, check out [Kickball's Awesome-Selfhosted li ### Badges I like badges! -[![Travis branch](https://img.shields.io/travis/firefly-iii/firefly-iii/master.svg?style=flat-square)](https://travis-ci.org/firefly-iii/firefly-iii/branches) [![Scrutinizer](https://img.shields.io/scrutinizer/g/firefly-iii/firefly-iii.svg?style=flat-square)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/) [![Coveralls github branch](https://img.shields.io/coveralls/github/firefly-iii/firefly-iii/master.svg?style=flat-square)](https://coveralls.io/github/firefly-iii/firefly-iii) [![Requires PHP7.1](https://img.shields.io/badge/php-7.1-red.svg?style=flat-square)](https://secure.php.net/downloads.php) [![license](https://img.shields.io/github/license/firefly-iii/firefly-iii.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?style=flat-square)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) +[![Travis branch](https://img.shields.io/travis/firefly-iii/firefly-iii/master.svg?style=flat-square)](https://travis-ci.org/firefly-iii/firefly-iii/branches) [![Scrutinizer](https://img.shields.io/scrutinizer/g/firefly-iii/firefly-iii.svg?style=flat-square)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/) [![Coveralls github branch](https://img.shields.io/coveralls/github/firefly-iii/firefly-iii/master.svg?style=flat-square)](https://coveralls.io/github/firefly-iii/firefly-iii) [![Requires PHP7.1](https://img.shields.io/badge/php-7.1-red.svg?style=flat-square)](https://secure.php.net/downloads.php) [![license](https://img.shields.io/github/license/firefly-iii/firefly-iii.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![Patreon page](https://img.shields.io/badge/patreon-JC5-brightgreen.svg?longCache=true&style=flat-square)](https://patreon.com/JC5) diff --git a/resources/lang/de_DE/config.php b/resources/lang/de_DE/config.php index a42da571da..7ddcf49b6e 100644 --- a/resources/lang/de_DE/config.php +++ b/resources/lang/de_DE/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'de', - 'locale' => 'de, Deutsch, de_DE, de_DE.utf8, de_DE.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e. %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e. %B %Y', - 'week_in_year' => 'KW %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'Do MMMM YYYY', - 'date_time_js' => 'Do MMMM YYYY um HH:mm:ss', - 'specific_day_js' => 'D. MMMM YYYY', - 'week_in_year_js' => '[Week]. KW, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q. Quartal YYYY', + 'html_language' => 'de', + 'locale' => 'de, Deutsch, de_DE, de_DE.utf8, de_DE.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e. %B %Y', + 'month_and_date_day' => '%A, %B %e. %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e. %B %Y', + 'week_in_year' => 'KW %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'Do MMMM YYYY', + 'date_time_js' => 'Do MMMM YYYY um HH:mm:ss', + 'specific_day_js' => 'D. MMMM YYYY', + 'week_in_year_js' => '[Week]. KW, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q. Quartal YYYY', + 'dow_1' => 'Montag', + 'dow_2' => 'Dienstag', + 'dow_3' => 'Mittwoch', + 'dow_4' => 'Donnerstag', + 'dow_5' => 'Freitag', + 'dow_6' => 'Samstag', + 'dow_7' => 'Sonntag', ]; diff --git a/resources/lang/de_DE/demo.php b/resources/lang/de_DE/demo.php index 479be2661f..8a65f1adcf 100644 --- a/resources/lang/de_DE/demo.php +++ b/resources/lang/de_DE/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Diese Ausgaben, Einnahmen und Umbuchungen sind nicht besonders einfallsreich. Sie wurden automatisch generiert.', 'piggy-banks-index' => 'Hier wurden bereits drei Sparschweine angelegt. Der Betrag in den Sparschweinen kann über die Plus-/Minus-Buttons angepasst werden. Klicken Sie auf den Namen des Sparschweins um weitere Informationen einzusehen.', 'import-index' => 'Jede CSV-Datei kann in Firefly III importiert werden. Es wird auch der Import von Daten aus Bunq und Spectre unterstützt. Weitere Banken und Finanzaggregatoren werden in Zukunft implementiert. Als Demo-Anwender können Sie jedoch nur einen „Schein”-Anbieter in Aktion erleben. Es werden einige zufällige Transaktionen generiert, um Ihnen zu zeigen, wie der Prozess funktioniert.', + 'recurring-index' => 'Bitte beachten Sie, dass sich diese Funktion in der aktiven Entwicklung befindet und möglicherweise nicht wie erwartet funktioniert.', + 'recurring-create' => 'Bitte beachten Sie, dass sich diese Funktion in der aktiven Entwicklung befindet und möglicherweise nicht wie erwartet funktioniert.', ]; diff --git a/resources/lang/de_DE/firefly.php b/resources/lang/de_DE/firefly.php index 6bbbcaf6de..89858113db 100644 --- a/resources/lang/de_DE/firefly.php +++ b/resources/lang/de_DE/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Scannen Sie den QR-Code mit einer Anwendung wie Authy oder Google Authenticator auf ihrem Handy und geben Sie den generierten Code ein.', 'pref_two_factor_auth_reset_code' => 'Verifizierungscode zurücksetzen', 'pref_two_factor_auth_disable_2fa' => '2FA deaktivieren', + '2fa_use_secret_instead' => 'Wenn Sie den QR-Code nicht scannen können, verwenden Sie stattdessen das Geheimnis: :secret.', 'pref_save_settings' => 'Einstellungen speichern', 'saved_preferences' => 'Einstellungen gespeichert!', 'preferences_general' => 'Allgemein', @@ -821,7 +822,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'language' => 'Sprache', 'new_savings_account' => ':bank_name-Sparkonto', 'cash_wallet' => 'Geldbörse', - 'currency_not_present' => 'Wenn die Währung, die Sie normalerweise verwenden, nicht aufgeführt ist, machen Sie sich keine Sorgen. Unter Optionen ➜ Währungen können Sie eigene Währungen anlegen.', + 'currency_not_present' => 'Wenn die Währung, die Sie normalerweise verwenden, nicht aufgeführt ist, machen Sie sich keine Sorgen. Unter Optionen ➜ Währungen können Sie eigene Währungen anlegen.', // home page: 'yourAccounts' => 'Deine Konten', @@ -901,7 +902,6 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'balanceEnd' => 'Bilanz zum Ende der Periode', 'splitByAccount' => 'Nach Konto aufteilen', 'coveredWithTags' => 'Mit Schlagwörtern versehen', - 'leftUnbalanced' => 'Unausgeglichen belassen', 'leftInBudget' => 'Verblieben im Kostenrahmen', 'sumOfSums' => 'Summe der Summen', 'noCategory' => '(keine Kategorie)', @@ -1063,7 +1063,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'instance_configuration' => 'Konfiguration', 'firefly_instance_configuration' => 'Konfigurationsoptionen für Firefly III', 'setting_single_user_mode' => 'Einzelnutzermodus', - 'setting_single_user_mode_explain' => 'Standardmäßig akzeptiert Firefly III nur eine Registrierung: Sie. Diese Sicherheitsmaßnahme verhindert das Benutzen Ihrer Instanz durch andere, außer Sie erlauben es. Zukünftige Registrierungen sind gesperrt. Wenn Sie dieses Kontrollkästchen deaktivieren können andere ihre Instanz ebenfalls benutzen, vorausgesetzt sie ist erreichbar (mit dem Internet verbunden).', + 'setting_single_user_mode_explain' => 'Dies ist eine sehr fortschrittliche Funktion, welche aber sehr nützlich sein kann. Stellen Sie sicher, dass Sie die Dokumentation (❓-Symbol in der oberen rechten Ecke) lesen, bevor Sie fortfahren.', 'store_configuration' => 'Konfiguration speichern', 'single_user_administration' => 'Benutzerverwaltung für :email', 'edit_user' => 'Benutzer :email bearbeiten', @@ -1135,6 +1135,8 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'is (partially) refunded by_inward' => 'wird (teilweise) erstattet durch', 'is (partially) paid for by_inward' => 'wird (teilweise) bezahlt von', 'is (partially) reimbursed by_inward' => 'wird (teilweise) erstattet durch', + 'inward_transaction' => 'Eingehende Zahlung', + 'outward_transaction' => 'Ausgehende Zahlung', 'relates to_outward' => 'bezieht sich auf', '(partially) refunds_outward' => '(Teil-)Erstattungen', '(partially) pays for_outward' => '(teilweise) bezahlt für', @@ -1156,8 +1158,9 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'cannot_convert_split_journal' => 'Eine Splitbuchung konnte nicht umgesetzt werden', // Import page (general strings only) - 'import_index_title' => 'Daten in Firefly III importieren', + 'import_index_title' => 'Buchungen in Firefly III importieren', 'import_data' => 'Daten importieren', + 'import_transactions' => 'Buchungen importieren', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Diese Funktion ist nicht verfügbar, wenn Sie Firefly III in einer Sandstorm.io-Umgebung verwenden.', @@ -1207,4 +1210,68 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'no_bills_intro_default' => 'Du hast noch keine Rechnungen. Sie können Rechnungen erstellen, um die laufenden Ausgaben, wie zum Beispiel Ihre Versicherung oder Miete, nachzuverfolgen.', 'no_bills_imperative_default' => 'Haben Sie regelmäßige Rechnungen? Erstellen Sie eine Rechnung und verfolgen Sie Ihre Zahlungen:', 'no_bills_create_default' => 'Eine Rechnung erstellen', + + // recurring transactions + 'recurrences' => 'Regelmäßige Buchungen', + 'no_recurring_title_default' => 'Lassen Sie uns eine regelmäßige Buchung erstellen!', + 'no_recurring_intro_default' => 'Sie verfügen noch über keine regelmäßigen Buchungen. Mit diesen können Sie Firefly III dazu einsetzen, automatisch Buchungen für Sie zu erstellen.', + 'no_recurring_imperative_default' => 'Dies ist eine sehr fortschrittliche Funktion, welche aber sehr nützlich sein kann. Stellen Sie sicher, dass Sie die Dokumentation (❓-Symbol in der oberen rechten Ecke) lesen, bevor Sie fortfahren.', + 'no_recurring_create_default' => 'Regelmäßige Buchung erstellen', + 'make_new_recurring' => 'Regelmäßige Buchung erstellen', + 'recurring_daily' => 'Täglich', + 'recurring_weekly' => 'Wöchentlich am :weekday', + 'recurring_monthly' => 'An jedem :dayOfMonth. Tag des Monats', + 'recurring_ndom' => 'An jedem :dayOfMonth. :weekday', + 'recurring_yearly' => 'Jährlich am :date', + 'overview_for_recurrence' => 'Übersicht der regelmäßigen Buchungen „:title”', + 'warning_duplicates_repetitions' => 'In seltenen Fällen werden die Daten zweimal in dieser Liste angezeigt. Dies kann passieren, wenn mehrere Wiederholungen aufeinandertreffen. Firefly III erzeugt immer eine Transaktion pro Tag.', + 'created_transactions' => 'Ähnliche Buchungen', + 'expected_Withdrawals' => 'Erwartete Abzüge', + 'expected_Deposits' => 'Erwartete Einzahlungen', + 'expected_Transfers' => 'Erwartete Überweisungen', + 'created_Withdrawals' => 'Erstellte Abzüge', + 'created_Deposits' => 'Erstellte Einzahlungen', + 'created_Transfers' => 'Erstellte Überweisungen', + 'created_from_recurrence' => 'Erstellt aus Dauerauftrag „:title” (#:id)', + + 'recurring_meta_field_tags' => 'Schlagwörter', + 'recurring_meta_field_notes' => 'Anmerkungen', + 'recurring_meta_field_bill_id' => 'Rechnung', + 'recurring_meta_field_piggy_bank_id' => 'Sparschwein', + 'create_new_recurrence' => 'Neuen Dauerauftrag erstellen', + 'help_first_date' => 'Geben Sie die erste erwartete Wiederholung an. Zeitpunkt muss in der Zukunft liegen.', + 'help_first_date_no_past' => 'Geben Sie die erste erwartete Wiederholung an. Firefly III erzeugt keine Buchungen die in der Vergangenheit liegen.', + 'no_currency' => '(ohne Währung)', + 'mandatory_for_recurring' => 'Erforderliche Wiederholungsinformationen', + 'mandatory_for_transaction' => 'Erforderliche Buchungsinformationen', + 'optional_for_recurring' => 'Optionale Wiederholungsinformationen', + 'optional_for_transaction' => 'Optionale Buchungsinformationen', + 'change_date_other_options' => 'Ändern Sie das „erste Datum”, um weitere Optionen anzuzeigen.', + 'mandatory_fields_for_tranaction' => 'Diese Werte enden in der/den zu erstellenden Buchung(en)', + 'click_for_calendar' => 'Klicken Sie hier für einen Kalender, der Ihnen anzeigt, wann sich die Buchung wiederholen würde.', + 'repeat_forever' => 'Wiederholt sich für immer', + 'repeat_until_date' => 'Wiederholen bis Datum', + 'repeat_times' => 'Wiederholen Sie mehrmals', + 'recurring_skips_one' => 'Alle anderen', + 'recurring_skips_more' => 'Überspringt :count Vorgänge', + 'store_new_recurrence' => 'Dauerauftrag speichern', + 'stored_new_recurrence' => 'Dauerauftrag „:title” erfolgreich gespeichert.', + 'edit_recurrence' => 'Dauerauftrag „:title” bearbeiten', + 'recurring_repeats_until' => 'Wiederholt sich bis :date', + 'recurring_repeats_forever' => 'Wiederholt sich für immer', + 'recurring_repeats_x_times' => 'Wiederholt sich :count mal', + 'update_recurrence' => 'Dauerauftrag aktualisieren', + 'updated_recurrence' => 'Dauerauftrag ":title" aktualisiert', + 'recurrence_is_inactive' => 'Dieser Dauerauftrag ist nicht aktiv und erzeugt keine neuen Buchungen.', + 'delete_recurring' => 'Dauerauftrag „:title” löschen', + 'new_recurring_transaction' => 'Neue Dauerauftrag', + 'help_weekend' => 'Was sollte Firefly III tun, wenn der Dauerauftrag auf einen Samstag oder Sonntag fällt?', + 'do_nothing' => 'Einfach die Buchung anlegen', + 'skip_transaction' => 'Vorkommen überspringen', + 'jump_to_friday' => 'Die Buchung stattdessen am vorhergehenden Freitag ausführen', + 'jump_to_monday' => 'Die Buchung stattdessen am darauffolgenden Montag ausführen', + 'will_jump_friday' => 'Wird am Freitag statt am Wochenende ausgeführt.', + 'will_jump_monday' => 'Wird am Montag statt am Wochenende ausgeführt.', + 'except_weekends' => 'Außer an Wochenenden', + 'recurrence_deleted' => 'Dauerauftrag „:title” gelöscht', ]; diff --git a/resources/lang/de_DE/form.php b/resources/lang/de_DE/form.php index 3195ec20e9..cc1d5e1783 100644 --- a/resources/lang/de_DE/form.php +++ b/resources/lang/de_DE/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Name der Bank', - 'bank_balance' => 'Kontostand', - 'savings_balance' => 'Sparguthaben', - 'credit_card_limit' => 'Kreditkartenlimit', - 'automatch' => 'Automatisch reagieren', - 'skip' => 'Überspringen', - 'name' => 'Name', - 'active' => 'Aktiv', - 'amount_min' => 'Mindestbetrag', - 'amount_max' => 'Höchstbetrag', - 'match' => 'Reagiert auf', - 'strict' => 'Strenger Modus', - 'repeat_freq' => 'Wiederholungen', - 'journal_currency_id' => 'Währung', - 'currency_id' => 'Währung', - 'transaction_currency_id' => 'Währung', - 'external_ip' => 'Die externe IP-Adresse Ihres Servers', - 'attachments' => 'Anhänge', - 'journal_amount' => 'Betrag', - 'journal_source_account_name' => 'Kreditor (Quelle)', - 'journal_source_account_id' => 'Bestandskonto (Quelle)', - 'BIC' => 'BIC', - 'verify_password' => 'Passwortsicherheit überprüfen', - 'source_account' => 'Quellkonto', - 'destination_account' => 'Zielkonto', - 'journal_destination_account_id' => 'Bestandskonto (Ziel)', - 'asset_destination_account' => 'Bestandskonto (Ziel)', - 'asset_source_account' => 'Bestandskonto (Quelle)', - 'journal_description' => 'Beschreibung', - 'note' => 'Notizen', - 'split_journal' => 'Diese Überweisung aufteilen', - 'split_journal_explanation' => 'Diese Überweisung in mehrere Teile aufteilen', - 'currency' => 'Währung', - 'account_id' => 'Bestandskonto', - 'budget_id' => 'Kostenrahmen', - 'openingBalance' => 'Eröffnungsbilanz', - 'tagMode' => 'Schlagwort-Modus', - 'tag_position' => 'Schlagwort-Speicherort', - 'virtualBalance' => 'Virtueller Kontostand', - 'targetamount' => 'Zielbetrag', - 'accountRole' => 'Rolle des Kontos', - 'openingBalanceDate' => 'Eröffnungsbilanzdatum', - 'ccType' => 'Zahlungsplan der Kreditkarte', - 'ccMonthlyPaymentDate' => 'Monatliches Zahlungsdatum der Kreditkarte', - 'piggy_bank_id' => 'Sparschwein', - 'returnHere' => 'Hierhin zurückkehren', - 'returnHereExplanation' => 'Nach dem Speichern hierher zurückkehren, um ein weiteres Element zu erstellen.', - 'returnHereUpdateExplanation' => 'Nach dem Update, hierher zurückkehren.', - 'description' => 'Beschreibung', - 'expense_account' => 'Debitor (Ausgabe)', - 'revenue_account' => 'Kreditor (Einnahme)', - 'decimal_places' => 'Nachkommastellen', - 'exchange_rate_instruction' => 'Fremdwährungen', - 'source_amount' => 'Betrag (Quelle)', - 'destination_amount' => 'Betrag (Ziel)', - 'native_amount' => 'Nativer Betrag', - 'new_email_address' => 'Neue E-Mail-Adresse', - 'verification' => 'Bestätigung', - 'api_key' => 'API-Schlüssel', - 'remember_me' => 'Angemeldet bleiben', + 'bank_name' => 'Name der Bank', + 'bank_balance' => 'Kontostand', + 'savings_balance' => 'Sparguthaben', + 'credit_card_limit' => 'Kreditkartenlimit', + 'automatch' => 'Automatisch reagieren', + 'skip' => 'Überspringen', + 'name' => 'Name', + 'active' => 'Aktiv', + 'amount_min' => 'Mindestbetrag', + 'amount_max' => 'Höchstbetrag', + 'match' => 'Reagiert auf', + 'strict' => 'Strenger Modus', + 'repeat_freq' => 'Wiederholungen', + 'journal_currency_id' => 'Währung', + 'currency_id' => 'Währung', + 'transaction_currency_id' => 'Währung', + 'external_ip' => 'Die externe IP-Adresse Ihres Servers', + 'attachments' => 'Anhänge', + 'journal_amount' => 'Betrag', + 'journal_source_name' => 'Erlöskonto (Herkunft)', + 'journal_source_id' => 'Anlagenkonto (Herkunft)', + 'BIC' => 'BIC', + 'verify_password' => 'Passwortsicherheit überprüfen', + 'source_account' => 'Quellkonto', + 'destination_account' => 'Zielkonto', + 'journal_destination_id' => 'Anlagenkonto (Ziel)', + 'asset_destination_account' => 'Bestandskonto (Ziel)', + 'asset_source_account' => 'Bestandskonto (Quelle)', + 'journal_description' => 'Beschreibung', + 'note' => 'Notizen', + 'split_journal' => 'Diese Überweisung aufteilen', + 'split_journal_explanation' => 'Diese Überweisung in mehrere Teile aufteilen', + 'currency' => 'Währung', + 'account_id' => 'Bestandskonto', + 'budget_id' => 'Kostenrahmen', + 'openingBalance' => 'Eröffnungsbilanz', + 'tagMode' => 'Schlagwort-Modus', + 'tag_position' => 'Schlagwort-Speicherort', + 'virtualBalance' => 'Virtueller Kontostand', + 'targetamount' => 'Zielbetrag', + 'accountRole' => 'Rolle des Kontos', + 'openingBalanceDate' => 'Eröffnungsbilanzdatum', + 'ccType' => 'Zahlungsplan der Kreditkarte', + 'ccMonthlyPaymentDate' => 'Monatliches Zahlungsdatum der Kreditkarte', + 'piggy_bank_id' => 'Sparschwein', + 'returnHere' => 'Hierhin zurückkehren', + 'returnHereExplanation' => 'Nach dem Speichern hierher zurückkehren, um ein weiteres Element zu erstellen.', + 'returnHereUpdateExplanation' => 'Nach dem Update, hierher zurückkehren.', + 'description' => 'Beschreibung', + 'expense_account' => 'Debitor (Ausgabe)', + 'revenue_account' => 'Kreditor (Einnahme)', + 'decimal_places' => 'Nachkommastellen', + 'exchange_rate_instruction' => 'Fremdwährungen', + 'source_amount' => 'Betrag (Quelle)', + 'destination_amount' => 'Betrag (Ziel)', + 'native_amount' => 'Nativer Betrag', + 'new_email_address' => 'Neue E-Mail-Adresse', + 'verification' => 'Bestätigung', + 'api_key' => 'API-Schlüssel', + 'remember_me' => 'Angemeldet bleiben', 'source_account_asset' => 'Quellkonto (Bestandskonto)', 'destination_account_expense' => 'Zielkonto (Unkostenkonto)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Ändere zu Einzahlung', 'convert_Transfer' => 'In Umbuchung umwandeln', - 'amount' => 'Betrag', - 'foreign_amount' => 'Ausländischer Betrag', - 'existing_attachments' => 'Bestehende Anhänge', - 'date' => 'Datum', - 'interest_date' => 'Zinstermin', - 'book_date' => 'Buchungsdatum', - 'process_date' => 'Bearbeitungsdatum', - 'category' => 'Kategorie', - 'tags' => 'Schlagwörter', - 'deletePermanently' => 'Dauerhaft löschen', - 'cancel' => 'Abbrechen', - 'targetdate' => 'Zieldatum', - 'startdate' => 'Startdatum', - 'tag' => 'Schlagwort', - 'under' => 'Unter', - 'symbol' => 'Zeichen', - 'code' => 'Schlüssel', - 'iban' => 'IBAN', - 'accountNumber' => 'Kontonummer', - 'creditCardNumber' => 'Kreditkartennummer', - 'has_headers' => 'Kopfzeilen', - 'date_format' => 'Datumsformat', - 'specifix' => 'Bank- oder Dateispezifischer Korrekturen', - 'attachments[]' => 'Anhänge', - 'store_new_withdrawal' => 'Speichere neue Ausgabe', - 'store_new_deposit' => 'Speichere neue Einnahme', - 'store_new_transfer' => 'Neue Umbuchung speichern', - 'add_new_withdrawal' => 'Fügen Sie eine neue Ausgabe hinzu', - 'add_new_deposit' => 'Fügen Sie eine neue Einnahme hinzu', - 'add_new_transfer' => 'Neue Umbuchung anlegen', - 'title' => 'Titel', - 'notes' => 'Notizen', - 'filename' => 'Dateiname', - 'mime' => 'MIME-Typ', - 'size' => 'Größe', - 'trigger' => 'Auslöser', - 'stop_processing' => 'Verarbeitung beenden', - 'start_date' => 'Anfang des Bereichs', - 'end_date' => 'Ende des Bereichs', - 'export_start_range' => 'Beginn des Exportbereichs', - 'export_end_range' => 'Ende des Exportbereichs', - 'export_format' => 'Dateiformat', - 'include_attachments' => 'Hochgeladene Anhänge hinzufügen', - 'include_old_uploads' => 'Importierte Daten hinzufügen', - 'accounts' => 'Exportiere die Überweisungen von diesem Konto', - 'delete_account' => 'Konto „:name” löschen', - 'delete_bill' => 'Rechnung „:name” löschen', - 'delete_budget' => 'Kostenrahmen „:name” löschen', - 'delete_category' => 'Kategorie „:name” löschen', - 'delete_currency' => 'Währung „:name” löschen', - 'delete_journal' => 'Lösche Überweisung mit Beschreibung ":description"', - 'delete_attachment' => 'Anhang „:name” löschen', - 'delete_rule' => 'Lösche Regel ":title"', - 'delete_rule_group' => 'Lösche Regelgruppe ":title"', - 'delete_link_type' => 'Verknüpfungstyp „:name” löschen', - 'delete_user' => 'Benutzer ":email" löschen', - 'user_areYouSure' => 'Wenn Sie den Benutzer ":email" löschen, ist alles weg. Es gibt keine Sicherung, Wiederherstellung oder ähnliches. Wenn Sie sich selbst löschen, verlieren Sie den Zugriff auf diese Instanz von Firefly III.', - 'attachment_areYouSure' => 'Möchten Sie den Anhang „:name” wirklich löschen?', - 'account_areYouSure' => 'Möchten Sie das Konto „:name” wirklich löschen?', - 'bill_areYouSure' => 'Möchten Sie die Rechnung „:name” wirklich löschen?', - 'rule_areYouSure' => 'Sind Sie sicher, dass Sie die Regel mit dem Titel ":title" löschen möchten?', - 'ruleGroup_areYouSure' => 'Sind Sie sicher, dass sie die Regelgruppe ":title" löschen möchten?', - 'budget_areYouSure' => 'Möchten Sie den Kostenrahmen „:name” wirklich löschen?', - 'category_areYouSure' => 'Möchten Sie die Kategorie „:name” wirklich löschen?', - 'currency_areYouSure' => 'Möchten Sie die Währung „:name” wirklich löschen?', - 'piggyBank_areYouSure' => 'Möchten Sie das Sparschwein „:name” wirklich löschen?', - 'journal_areYouSure' => 'Sind Sie sicher, dass Sie die Überweisung mit dem Namen ":description" löschen möchten?', - 'mass_journal_are_you_sure' => 'Sind Sie sicher, dass Sie diese Überweisung löschen möchten?', - 'tag_areYouSure' => 'Möchten Sie das Schlagwort „:tag” wirklich löschen?', - 'journal_link_areYouSure' => 'Sind Sie sicher, dass Sie die Verknüpfung zwischen :source und :destination löschen möchten?', - 'linkType_areYouSure' => 'Möchten Sie den Verknüpfungstyp „:name” („:inward”/„:outward”) wirklich löschen?', - 'permDeleteWarning' => 'Das Löschen von Dingen in Firefly III ist dauerhaft und kann nicht rückgängig gemacht werden.', - 'mass_make_selection' => 'Sie können das Löschen von Elementen verhindern, indem Sie die Checkbox entfernen.', - 'delete_all_permanently' => 'Ausgewähltes dauerhaft löschen', - 'update_all_journals' => 'Diese Transaktionen aktualisieren', - 'also_delete_transactions' => 'Die einzige Überweisung, die mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Überweisungen, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', - 'also_delete_connections' => 'Die einzige Transaktion, die mit diesem Verknüpfungstyp verknüpft ist, verliert diese Verbindung. • Alle :count Buchungen, die mit diesem Verknüpfungstyp verknüpft sind, verlieren ihre Verbindung.', - 'also_delete_rules' => 'Die einzige Regel, die mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Regeln, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', - 'also_delete_piggyBanks' => 'Das einzige Sparschwein, das mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Sparschweine, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', - 'bill_keep_transactions' => 'Die einzige Überweisung, die mit dieser Rechnung verknüpft ist, wird nicht gelöscht. | Keine der :count Überweisungen, die mit dieser Rechnung verknüpft sind, werden gelöscht.', - 'budget_keep_transactions' => 'Die einzige Buchung, die mit dieser Rechnung verknüpft ist, wird nicht gelöscht. | Keine der :count Buchungen, die mit dieser Rechnung verknüpft sind, werden gelöscht.', - 'category_keep_transactions' => 'Die eine Überweisungen, die mit dieser Kategorie verknüpft ist, wird nicht gelöscht. | Keine der :count Kategorien, die mit dieser Rechnung verknüpft sind, werden gelöscht.', - 'tag_keep_transactions' => 'Die einzige Buchung, die mit diesem Schlagwort verbunden ist, wird nicht gelöscht. • Alle :count Vorgänge, die mit diesem Schlagwort verbunden sind, werden nicht gelöscht.', - 'check_for_updates' => 'Nach Updates suchen', + 'amount' => 'Betrag', + 'foreign_amount' => 'Ausländischer Betrag', + 'existing_attachments' => 'Bestehende Anhänge', + 'date' => 'Datum', + 'interest_date' => 'Zinstermin', + 'book_date' => 'Buchungsdatum', + 'process_date' => 'Bearbeitungsdatum', + 'category' => 'Kategorie', + 'tags' => 'Schlagwörter', + 'deletePermanently' => 'Dauerhaft löschen', + 'cancel' => 'Abbrechen', + 'targetdate' => 'Zieldatum', + 'startdate' => 'Startdatum', + 'tag' => 'Schlagwort', + 'under' => 'Unter', + 'symbol' => 'Zeichen', + 'code' => 'Schlüssel', + 'iban' => 'IBAN', + 'accountNumber' => 'Kontonummer', + 'creditCardNumber' => 'Kreditkartennummer', + 'has_headers' => 'Kopfzeilen', + 'date_format' => 'Datumsformat', + 'specifix' => 'Bank- oder Dateispezifischer Korrekturen', + 'attachments[]' => 'Anhänge', + 'store_new_withdrawal' => 'Speichere neue Ausgabe', + 'store_new_deposit' => 'Speichere neue Einnahme', + 'store_new_transfer' => 'Neue Umbuchung speichern', + 'add_new_withdrawal' => 'Fügen Sie eine neue Ausgabe hinzu', + 'add_new_deposit' => 'Fügen Sie eine neue Einnahme hinzu', + 'add_new_transfer' => 'Neue Umbuchung anlegen', + 'title' => 'Titel', + 'notes' => 'Notizen', + 'filename' => 'Dateiname', + 'mime' => 'MIME-Typ', + 'size' => 'Größe', + 'trigger' => 'Auslöser', + 'stop_processing' => 'Verarbeitung beenden', + 'start_date' => 'Anfang des Bereichs', + 'end_date' => 'Ende des Bereichs', + 'export_start_range' => 'Beginn des Exportbereichs', + 'export_end_range' => 'Ende des Exportbereichs', + 'export_format' => 'Dateiformat', + 'include_attachments' => 'Hochgeladene Anhänge hinzufügen', + 'include_old_uploads' => 'Importierte Daten hinzufügen', + 'accounts' => 'Exportiere die Überweisungen von diesem Konto', + 'delete_account' => 'Konto „:name” löschen', + 'delete_bill' => 'Rechnung „:name” löschen', + 'delete_budget' => 'Kostenrahmen „:name” löschen', + 'delete_category' => 'Kategorie „:name” löschen', + 'delete_currency' => 'Währung „:name” löschen', + 'delete_journal' => 'Lösche Überweisung mit Beschreibung ":description"', + 'delete_attachment' => 'Anhang „:name” löschen', + 'delete_rule' => 'Lösche Regel ":title"', + 'delete_rule_group' => 'Lösche Regelgruppe ":title"', + 'delete_link_type' => 'Verknüpfungstyp „:name” löschen', + 'delete_user' => 'Benutzer ":email" löschen', + 'delete_recurring' => 'Dauerauftrag „:title” löschen', + 'user_areYouSure' => 'Wenn Sie den Benutzer ":email" löschen, ist alles weg. Es gibt keine Sicherung, Wiederherstellung oder ähnliches. Wenn Sie sich selbst löschen, verlieren Sie den Zugriff auf diese Instanz von Firefly III.', + 'attachment_areYouSure' => 'Möchten Sie den Anhang „:name” wirklich löschen?', + 'account_areYouSure' => 'Möchten Sie das Konto „:name” wirklich löschen?', + 'bill_areYouSure' => 'Möchten Sie die Rechnung „:name” wirklich löschen?', + 'rule_areYouSure' => 'Sind Sie sicher, dass Sie die Regel mit dem Titel ":title" löschen möchten?', + 'ruleGroup_areYouSure' => 'Sind Sie sicher, dass sie die Regelgruppe ":title" löschen möchten?', + 'budget_areYouSure' => 'Möchten Sie den Kostenrahmen „:name” wirklich löschen?', + 'category_areYouSure' => 'Möchten Sie die Kategorie „:name” wirklich löschen?', + 'recurring_areYouSure' => 'Möchten Sie den Dauerauftrag „:title” wirklich löschen?', + 'currency_areYouSure' => 'Möchten Sie die Währung „:name” wirklich löschen?', + 'piggyBank_areYouSure' => 'Möchten Sie das Sparschwein „:name” wirklich löschen?', + 'journal_areYouSure' => 'Sind Sie sicher, dass Sie die Überweisung mit dem Namen ":description" löschen möchten?', + 'mass_journal_are_you_sure' => 'Sind Sie sicher, dass Sie diese Überweisung löschen möchten?', + 'tag_areYouSure' => 'Möchten Sie das Schlagwort „:tag” wirklich löschen?', + 'journal_link_areYouSure' => 'Sind Sie sicher, dass Sie die Verknüpfung zwischen :source und :destination löschen möchten?', + 'linkType_areYouSure' => 'Möchten Sie den Verknüpfungstyp „:name” („:inward”/„:outward”) wirklich löschen?', + 'permDeleteWarning' => 'Das Löschen von Dingen in Firefly III ist dauerhaft und kann nicht rückgängig gemacht werden.', + 'mass_make_selection' => 'Sie können das Löschen von Elementen verhindern, indem Sie die Checkbox entfernen.', + 'delete_all_permanently' => 'Ausgewähltes dauerhaft löschen', + 'update_all_journals' => 'Diese Transaktionen aktualisieren', + 'also_delete_transactions' => 'Die einzige Überweisung, die mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Überweisungen, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', + 'also_delete_connections' => 'Die einzige Transaktion, die mit diesem Verknüpfungstyp verknüpft ist, verliert diese Verbindung. • Alle :count Buchungen, die mit diesem Verknüpfungstyp verknüpft sind, verlieren ihre Verbindung.', + 'also_delete_rules' => 'Die einzige Regel, die mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Regeln, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', + 'also_delete_piggyBanks' => 'Das einzige Sparschwein, das mit diesem Konto verknüpft ist, wird ebenfalls gelöscht. | Alle :count Sparschweine, die mit diesem Konto verknüpft sind, werden ebenfalls gelöscht.', + 'bill_keep_transactions' => 'Die einzige mit dieser Rechnung verbundene Buchung wird nicht gelöscht. | Alle :count Buchungen, die mit dieser Rechnung verbunden sind, werden nicht gelöscht.', + 'budget_keep_transactions' => 'Die einzige mit diesem Kostenrahmen verbundene Buchung wird nicht gelöscht. | Alle :count Buchungen, die mit diesem Kostenrahmen verbunden sind, werden nicht gelöscht.', + 'category_keep_transactions' => 'Die einzige Buchung, die mit dieser Kategorie verbunden ist, wird nicht gelöscht. | Alle :count Buchungen, die mit dieser Kategorie verbunden sind, werden nicht gelöscht.', + 'recurring_keep_transactions' => 'Die einzige Buchung, die durch diesen Dauerauftrag erstellt wurde, wird nicht gelöscht. | Alle :count Buchungen, die durch diesen Dauerauftrag erstellt wurden, werden nicht gelöscht.', + 'tag_keep_transactions' => 'Das einzige mit dieser Rechnung verbundene Schlagwort wird nicht gelöscht. | Alle :count Schlagwörter, die mit dieser Rechnung verbunden sind, werden nicht gelöscht.', + 'check_for_updates' => 'Nach Updates suchen', 'email' => 'E-Mail Adresse', 'password' => 'Passwort', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Ländercode', 'provider_code' => 'Bank oder Datenanbieter', - 'due_date' => 'Fälligkeitstermin', - 'payment_date' => 'Zahlungsdatum', - 'invoice_date' => 'Rechnungsdatum', - 'internal_reference' => 'Interner Verweis', - 'inward' => 'Beschreibung der Eingänge', - 'outward' => 'Beschreibung der Ausgänge', - 'rule_group_id' => 'Regelgruppe', + 'due_date' => 'Fälligkeitstermin', + 'payment_date' => 'Zahlungsdatum', + 'invoice_date' => 'Rechnungsdatum', + 'internal_reference' => 'Interner Verweis', + 'inward' => 'Beschreibung der Eingänge', + 'outward' => 'Beschreibung der Ausgänge', + 'rule_group_id' => 'Regelgruppe', + 'transaction_description' => 'Beschreibung der Buchung', + 'first_date' => 'Erstes Datum', + 'transaction_type' => 'Art der Buchung', + 'repeat_until' => 'Wiederholen bis', + 'recurring_description' => 'Beschreibung des Dauerauftrags', + 'repetition_type' => 'Art der Wiederholung', + 'foreign_currency_id' => 'Fremdwährung', + 'repetition_end' => 'Wiederholung endet', + 'repetitions' => 'Wiederholungen', + 'calendar' => 'Kalender', + 'weekend' => 'Wochenende', + ]; diff --git a/resources/lang/de_DE/import.php b/resources/lang/de_DE/import.php index 2e2c30e8e5..e19c46222c 100644 --- a/resources/lang/de_DE/import.php +++ b/resources/lang/de_DE/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'Es scheint, dass Sie keine Konten zum Importieren ausgewählt haben.', 'imported_from_account' => 'Von „:account” importiert', 'spectre_account_with_number' => 'Konto :number', + 'job_config_spectre_apply_rules' => 'Regeln übernehmen', + 'job_config_spectre_apply_rules_text' => 'Standardmäßig werden Ihre Regeln auf die Buchungen angewendet, die während dieser Importfunktion erstellt wurden. Wenn Sie dies nicht wünschen, entfernen Sie die Markierung dieses Kontrollkästchens.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq-Konten', 'job_config_bunq_accounts_text' => 'Dies sind jene Konten, die mit Ihrem „bunq”-Konto verknüpft sind. Bitte wählen Sie die Konten aus, von denen Sie importieren möchten, und in welches Konto die Buchungen importiert werden sollen.', 'bunq_no_mapping' => 'Es scheint, dass Sie keine Konten ausgewählt haben.', 'should_download_config' => 'Sie sollten die Konfigurationsdatei für diesen Aufgabe herunterladen. Dies wird zukünftige Importe erheblich erleichtern.', 'share_config_file' => 'Wenn Sie Daten von einer öffentlichen Bank importiert haben, sollten Sie Ihre Konfigurationsdatei freigeben, damit es für andere Benutzer einfach ist, ihre Daten zu importieren. Wenn Sie Ihre Konfigurationsdatei freigeben, werden Ihre finanziellen Daten nicht preisgegeben.', - + 'job_config_bunq_apply_rules' => 'Regeln übernehmen', + 'job_config_bunq_apply_rules_text' => 'Standardmäßig werden Ihre Regeln auf die Buchungen angewendet, die während dieser Importfunktion erstellt wurden. Wenn Sie dies nicht wünschen, entfernen Sie die Markierung dieses Kontrollkästchens.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT Code', diff --git a/resources/lang/de_DE/list.php b/resources/lang/de_DE/list.php index f2d030e3f7..fe4b250ed6 100644 --- a/resources/lang/de_DE/list.php +++ b/resources/lang/de_DE/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Letzte Anmeldung', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq-Zahlungskennung', + 'repetitions' => 'Wiederholungen', + 'title' => 'Titel', + 'transaction_s' => 'Buchung(en)', + 'field' => 'Feld', + 'value' => 'Wert', ]; diff --git a/resources/lang/de_DE/validation.php b/resources/lang/de_DE/validation.php index 7cc5d3de94..d30156cb75 100644 --- a/resources/lang/de_DE/validation.php +++ b/resources/lang/de_DE/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Dies ist keine gültige IBAN.', - 'source_equals_destination' => 'Das Quellkonto entspricht dem Zielkonto', + 'source_equals_destination' => 'Das Quellkonto entspricht dem Zielkonto.', 'unique_account_number_for_user' => 'Diese Kontonummer scheint bereits verwendet zu sein.', 'unique_iban_for_user' => 'Dieser IBAN scheint bereits verwendet zu werden.', 'deleted_user' => 'Aufgrund von Sicherheitsbeschränkungen ist eine Registrierung mit dieser E-Mail-Adresse nicht zugelassen.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Datei „:name” erfolgreich hochgeladen.', 'must_exist' => 'Die ID in Feld :attribute existiert nicht in der Datenbank.', 'all_accounts_equal' => 'Alle Konten in diesem Feld müssen identisch sein.', - 'invalid_selection' => 'Die Auswahl ist ungültig', + 'invalid_selection' => 'Ihre Auswahl ist ungültig.', 'belongs_user' => 'Dieser Wert ist für dieses Feld ungültig.', 'at_least_one_transaction' => 'Sie brauchen mindestens eine Transaktion.', + 'at_least_one_repetition' => 'Mindestens eine Wiederholung erforderlich.', + 'require_repeat_until' => 'Erfordert entweder eine Anzahl von Wiederholungen oder ein Enddatum (repeat_until). Nicht beides.', 'require_currency_info' => 'Der Inhalt dieses Feldes ist ohne Währungsinformationen ungültig.', 'equal_description' => 'Die Transaktionsbeschreibung darf nicht der globalen Beschreibung entsprechen.', 'file_invalid_mime' => 'Die Datei „:name” ist vom Typ „:mime”, welcher nicht zum Hochladen zugelassen ist.', 'file_too_large' => 'Die Datei „:name” ist zu groß.', - 'belongs_to_user' => 'Der Wert von :attribute ist nicht bekannt', + 'belongs_to_user' => 'Der Wert von :attribute ist unbekannt.', 'accepted' => ':attribute muss akzeptiert werden.', 'bic' => 'Dies ist kein gültiger BIC.', + 'at_least_one_trigger' => 'Regel muss mindestens einen Auslöser enthalten', + 'at_least_one_action' => 'Regel muss mindestens eine Aktion enthalten', + 'base64' => 'Dies sind keine gültigen base64-kodierten Daten.', + 'model_id_invalid' => 'Die angegebene ID scheint für dieses Modell ungültig zu sein.', 'more' => ':attribute muss größer als Null sein.', 'active_url' => ':attribute ist keine gültige URL.', 'after' => ':attribute muss ein Datum nach :date sein.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute muss eine Liste sein.', 'unique_for_user' => 'Es gibt bereits einen Eintrag mit diesem :attribute.', 'before' => ':attribute muss ein Datum vor dem :date sein.', - 'unique_object_for_user' => 'Der Name wird bereits verwendet', - 'unique_account_for_user' => 'Dieser Kontoname wird bereits verwendet', + 'unique_object_for_user' => 'Dieser Name wird bereits verwendet.', + 'unique_account_for_user' => 'Dieser Kontoname wird bereits verwendet.', 'between.numeric' => ':attribute muss zwischen :min und :max liegen.', 'between.file' => ':attribute muss zwischen :min und :max Kilobytes groß sein.', 'between.string' => ':attribute muss zwischen :min und :max Zeichen lang sein.', @@ -85,6 +91,9 @@ return [ 'min.array' => ':attribute muss mindestens :min Elemente enthalten.', 'not_in' => ':attribute ist ungültig.', 'numeric' => ':attribute muss eine Zahl sein.', + 'numeric_native' => 'Die native Betrag muss eine Zahl sein.', + 'numeric_destination' => 'Der Zielbeitrag muss eine Zahl sein.', + 'numeric_source' => 'Der Quellbetrag muss eine Zahl sein.', 'regex' => 'Das Format von :attribute ist ungültig.', 'required' => ':attribute Feld muss ausgefüllt sein.', 'required_if' => ':attribute Feld ist notwendig, wenn :other :value entspricht.', @@ -109,9 +118,12 @@ return [ 'file' => 'Das :attribute muss eine Datei sein.', 'in_array' => ':attribute existiert nicht in :other.', 'present' => 'Das :attribute Feld muss vorhanden sein.', - 'amount_zero' => 'Der Gesamtbetrag darf nicht Null sein', + 'amount_zero' => 'Der Gesamtbetrag darf nicht Null sein.', 'unique_piggy_bank_for_user' => 'Der Name des Sparschweins muss eindeutig sein.', - 'secure_password' => 'Das ist kein sicheres Passwort. Bitte versuchen Sie es erneut. Weitere Informationen finden Sie unter https://github.com/firefly-iii/help/wiki/Secure-password', + 'secure_password' => 'Dies ist kein sicheres Passwort. Bitte versuchen Sie es erneut. Weitere Informationen finden Sie unter https://github.com/firefly-iii/help/wiki/Secure-password (engl.).', + 'valid_recurrence_rep_type' => 'Ungültige Wiederholungsart für Daueraufträge.', + 'valid_recurrence_rep_moment' => 'Ungültiges Wiederholungsmoment für diese Art der Wiederholung.', + 'invalid_account_info' => 'Ungültige Kontodaten.', 'attributes' => [ 'email' => 'E-Mail Adresse', 'description' => 'Beschreibung', diff --git a/resources/lang/en_US/config.php b/resources/lang/en_US/config.php index f7a905591f..eb6b1f2322 100644 --- a/resources/lang/en_US/config.php +++ b/resources/lang/en_US/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'en', - 'locale' => 'en, English, en_US, en_US.utf8, en_US.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' => 'Week %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'en', + 'locale' => 'en, English, en_US, en_US.utf8, en_US.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%B %e, %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%B %e, %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Week %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'MMMM Do, YYYY', + 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Monday', + 'dow_2' => 'Tuesday', + 'dow_3' => 'Wednesday', + 'dow_4' => 'Thursday', + 'dow_5' => 'Friday', + 'dow_6' => 'Saturday', + 'dow_7' => 'Sunday', ]; diff --git a/resources/lang/en_US/demo.php b/resources/lang/en_US/demo.php index 640ab10298..e56f6f24c6 100644 --- a/resources/lang/en_US/demo.php +++ b/resources/lang/en_US/demo.php @@ -34,4 +34,6 @@ return [ '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' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index bd4b5d4bd1..ad47b99796 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -465,6 +465,7 @@ return [ '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_disable_2fa' => 'Disable 2FA', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Save settings', 'saved_preferences' => 'Preferences saved!', 'preferences_general' => 'General', @@ -820,7 +821,7 @@ return [ 'language' => 'Language', 'new_savings_account' => ':bank_name savings account', 'cash_wallet' => 'Cash wallet', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', // home page: 'yourAccounts' => 'Your accounts', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Balance at end of period', 'splitByAccount' => 'Split by account', 'coveredWithTags' => 'Covered with tags', - 'leftUnbalanced' => 'Left unbalanced', 'leftInBudget' => 'Left in budget', 'sumOfSums' => 'Sum of sums', 'noCategory' => '(no category)', @@ -1062,7 +1062,7 @@ return [ 'instance_configuration' => 'Configuration', 'firefly_instance_configuration' => 'Configuration options for 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).', + '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 well, 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', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => 'is (partially) refunded by', 'is (partially) paid for by_inward' => 'is (partially) paid for by', 'is (partially) reimbursed by_inward' => 'is (partially) reimbursed by', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'relates to', '(partially) refunds_outward' => '(partially) refunds', '(partially) pays for_outward' => '(partially) pays for', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Cannot convert a split transaction', // Import page (general strings only) - 'import_index_title' => 'Import data into Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Import data', + 'import_transactions' => 'Import transactions', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'You have no bills yet. You can create bills to keep track of regular expenses, like your rent or 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', + + // recurring transactions + 'recurrences' => 'Recurring transactions', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Create a recurring transaction', + 'make_new_recurring' => 'Create a recurring transaction', + 'recurring_daily' => 'Every day', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Related transactions', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notes', + 'recurring_meta_field_bill_id' => 'Bill', + 'recurring_meta_field_piggy_bank_id' => 'Piggy bank', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 39955eb731..fe18364b8e 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => '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', - 'strict' => 'Strict mode', - 'repeat_freq' => 'Repeats', - 'journal_currency_id' => 'Currency', - 'currency_id' => 'Currency', - 'transaction_currency_id' => 'Currency', - 'external_ip' => 'Your server\'s external IP', - 'attachments' => 'Attachments', - 'journal_amount' => 'Amount', - 'journal_source_account_name' => 'Revenue account (source)', - 'journal_source_account_id' => 'Asset account (source)', - 'BIC' => 'BIC', - 'verify_password' => 'Verify password security', - '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', - 'tag_position' => 'Tag location', - 'virtualBalance' => 'Virtual balance', - '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', - 'source_amount' => 'Amount (source)', - 'destination_amount' => 'Amount (destination)', - 'native_amount' => 'Native amount', - 'new_email_address' => 'New email address', - 'verification' => 'Verification', - 'api_key' => 'API key', - 'remember_me' => 'Remember me', + 'bank_name' => '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', + 'strict' => 'Strict mode', + 'repeat_freq' => 'Repeats', + 'journal_currency_id' => 'Currency', + 'currency_id' => 'Currency', + 'transaction_currency_id' => 'Currency', + 'external_ip' => 'Your server\'s external IP', + 'attachments' => 'Attachments', + 'journal_amount' => 'Amount', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Verify password security', + 'source_account' => 'Source account', + 'destination_account' => 'Destination account', + 'journal_destination_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', + 'tag_position' => 'Tag location', + 'virtualBalance' => 'Virtual balance', + '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', + 'source_amount' => 'Amount (source)', + 'destination_amount' => 'Amount (destination)', + 'native_amount' => 'Native amount', + 'new_email_address' => 'New email address', + 'verification' => 'Verification', + 'api_key' => 'API key', + 'remember_me' => 'Remember me', 'source_account_asset' => 'Source account (asset account)', 'destination_account_expense' => 'Destination account (expense account)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Convert deposit', 'convert_Transfer' => 'Convert transfer', - 'amount' => 'Amount', - 'foreign_amount' => 'Foreign amount', - 'existing_attachments' => 'Existing attachments', - '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', - 'startdate' => 'Start date', - 'tag' => 'Tag', - 'under' => 'Under', - 'symbol' => 'Symbol', - 'code' => 'Code', - 'iban' => 'IBAN', - 'accountNumber' => 'Account number', - 'creditCardNumber' => 'Credit card 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', - '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"', - 'delete_link_type' => 'Delete link type ":name"', - 'delete_user' => 'Delete user ":email"', - 'user_areYouSure' => 'If you delete user ":email", everything will be gone. There is no undo, undelete or anything. If you delete yourself, you will lose access to this instance of Firefly III.', - '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"?', - 'journal_link_areYouSure' => 'Are you sure you want to delete the link between :source and :destination?', - 'linkType_areYouSure' => 'Are you sure you want to delete the link type ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Deleting stuff from Firefly III 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_connections' => 'The only transaction linked with this link type will lose this connection.|All :count transactions linked with this link type will lose their connection.', - '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.', - 'check_for_updates' => 'Check for updates', + 'amount' => 'Amount', + 'foreign_amount' => 'Foreign amount', + 'existing_attachments' => 'Existing attachments', + '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', + 'startdate' => 'Start date', + 'tag' => 'Tag', + 'under' => 'Under', + 'symbol' => 'Symbol', + 'code' => 'Code', + 'iban' => 'IBAN', + 'accountNumber' => 'Account number', + 'creditCardNumber' => 'Credit card 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', + '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"', + 'delete_link_type' => 'Delete link type ":name"', + 'delete_user' => 'Delete user ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'If you delete user ":email", everything will be gone. There is no undo, undelete or anything. If you delete yourself, you will lose access to this instance of Firefly III.', + '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"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + '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"?', + 'journal_link_areYouSure' => 'Are you sure you want to delete the link between :source and :destination?', + 'linkType_areYouSure' => 'Are you sure you want to delete the link type ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Deleting stuff from Firefly III 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_connections' => 'The only transaction linked with this link type will lose this connection.|All :count transactions linked with this link type will lose their connection.', + '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 be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Check for updates', 'email' => 'Email address', 'password' => 'Password', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Country code', 'provider_code' => 'Bank or data-provider', - 'due_date' => 'Due date', - 'payment_date' => 'Payment date', - 'invoice_date' => 'Invoice date', - 'internal_reference' => 'Internal reference', - 'inward' => 'Inward description', - 'outward' => 'Outward description', - 'rule_group_id' => 'Rule group', + 'due_date' => 'Due date', + 'payment_date' => 'Payment date', + 'invoice_date' => 'Invoice date', + 'internal_reference' => 'Internal reference', + 'inward' => 'Inward description', + 'outward' => 'Outward description', + 'rule_group_id' => 'Rule group', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php index feb4f58645..824704689e 100644 --- a/resources/lang/en_US/import.php +++ b/resources/lang/en_US/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index bd11777b11..171a7acf23 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Last login', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq payment ID', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transaction(s)', + 'field' => 'Field', + 'value' => 'Value', ]; diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 9ca9ec0f61..b573da5844 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'This is not a valid IBAN.', - 'source_equals_destination' => 'The source account equals the destination account', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'It looks like this account number is already in use.', 'unique_iban_for_user' => 'It looks like this IBAN is already in use.', 'deleted_user' => 'Due to security constraints, you cannot register using this email address.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Succesfully uploaded file ":name".', 'must_exist' => 'The ID in field :attribute does not exist in the database.', 'all_accounts_equal' => 'All accounts in this field must be equal.', - 'invalid_selection' => 'Your selection is invalid', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'This value is invalid for this field.', 'at_least_one_transaction' => 'Need at least one transaction.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'The content of this field is invalid without currency information.', 'equal_description' => 'Transaction description should not equal global description.', 'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.', 'file_too_large' => 'File ":name" is too large.', - 'belongs_to_user' => 'The value of :attribute is unknown', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => 'The :attribute must be accepted.', 'bic' => 'This is not a valid BIC.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', '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.', @@ -53,8 +59,8 @@ return [ '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', + '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.', @@ -85,6 +91,9 @@ return [ 'min.array' => 'The :attribute must have at least :min items.', 'not_in' => 'The selected :attribute is invalid.', 'numeric' => 'The :attribute must be a number.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount 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.', @@ -109,9 +118,12 @@ return [ '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', + 'amount_zero' => 'The total amount cannot be zero.', 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'email address', 'description' => 'description', diff --git a/resources/lang/es_ES/config.php b/resources/lang/es_ES/config.php index d5bebf299c..f4981713fe 100644 --- a/resources/lang/es_ES/config.php +++ b/resources/lang/es_ES/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'es', - 'locale' => 'es, Spanish, es_ES, es_ES.utf8, es_ES.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' => 'Semana %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'es', + 'locale' => 'es, Spanish, es_ES, es_ES.utf8, es_ES.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%B %e, %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%B %e, %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Semana %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'MMMM Do, YYYY', + 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Monday', + 'dow_2' => 'Tuesday', + 'dow_3' => 'Wednesday', + 'dow_4' => 'Thursday', + 'dow_5' => 'Friday', + 'dow_6' => 'Saturday', + 'dow_7' => 'Sunday', ]; diff --git a/resources/lang/es_ES/demo.php b/resources/lang/es_ES/demo.php index 67a5f2fe85..4588444cea 100644 --- a/resources/lang/es_ES/demo.php +++ b/resources/lang/es_ES/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Estos gastos, depósitos y transferencias no son particularmente imaginativos. Se han generado automáticamente.', 'piggy-banks-index' => 'Como puede ver, hay tres alcancías. Utilice los botones más y menos para influir en la cantidad de dinero en cada alcancía. Haga clic en el nombre de la alcancía para ver la administración de cada una.', 'import-index' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/es_ES/firefly.php b/resources/lang/es_ES/firefly.php index 70fe05fe79..4d83113a79 100644 --- a/resources/lang/es_ES/firefly.php +++ b/resources/lang/es_ES/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Escanee el código QR con una aplicación en su teléfono como Authy o Google autenticator y ingrese el código generado.', 'pref_two_factor_auth_reset_code' => 'Reiniciar código de verificación', 'pref_two_factor_auth_disable_2fa' => 'Disable 2FA', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Guardar la configuración', 'saved_preferences' => '¡Preferencias guardadas!', 'preferences_general' => 'General', @@ -821,7 +822,7 @@ return [ 'language' => 'Language', 'new_savings_account' => ':bank_name savings account', 'cash_wallet' => 'Cash wallet', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', // home page: 'yourAccounts' => 'Tus cuentas', @@ -901,7 +902,6 @@ return [ 'balanceEnd' => 'Balance al final de un periodo', 'splitByAccount' => 'Separada por cuenta', 'coveredWithTags' => 'Cubierta con etiquetas', - 'leftUnbalanced' => 'Izquierda desbalanceada', 'leftInBudget' => 'Dejado en el presupuesto', 'sumOfSums' => 'Suma de sumas', 'noCategory' => '(sin categoría)', @@ -1063,7 +1063,7 @@ return [ 'instance_configuration' => 'Configuracion', 'firefly_instance_configuration' => 'Opciones de configuración de Firefly III', 'setting_single_user_mode' => 'Modo de usuario único', - 'setting_single_user_mode_explain' => 'Por defecto, Firefly III solo acepta un (1) registro: Usted. Esta es una medida de seguridad que impide que otros utilicen su instancia a menos que usted lo permita. Los registros futuros están bloqueados. Cuando usted desbloquee esta casilla, otros pueden usar su instancia también, suponiendo que puedan alcanzarla ( cuando este conectada a Internet).', + '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 well, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Configuración de tienda', 'single_user_administration' => 'Administración de usuarios para :email', 'edit_user' => 'Editar usuario :email', @@ -1135,6 +1135,8 @@ return [ 'is (partially) refunded by_inward' => 'es (parcialmente) es devuelto por', 'is (partially) paid for by_inward' => 'es(parcialmente) pagado por', 'is (partially) reimbursed by_inward' => 'es(parcialmente) reembolsado por', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'relacionado con', '(partially) refunds_outward' => '(parcialmente) reembolso', '(partially) pays for_outward' => '(parcialmente) paga por', @@ -1156,8 +1158,9 @@ return [ 'cannot_convert_split_journal' => 'No se puede convertir una transacción dividida', // Import page (general strings only) - 'import_index_title' => 'Importar datos a Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Importar datos', + 'import_transactions' => 'Import transactions', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Esta función no esta disponible cuando usted esta utilizando Firefly III dentro de un ambiente Sandstorm.io.', @@ -1207,4 +1210,68 @@ return [ 'no_bills_intro_default' => 'Usted no tiene facturas aun. Usted puede crear facturas para hacer un seguimiento de los gastos regulares, como su alquiler o el seguro.', 'no_bills_imperative_default' => '¿Tienes facturas periódicas? Crea una factura y haz un seguimiento de tus pagos:', 'no_bills_create_default' => 'Crear una factura', + + // recurring transactions + 'recurrences' => 'Recurring transactions', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Create a recurring transaction', + 'make_new_recurring' => 'Create a recurring transaction', + 'recurring_daily' => 'Every day', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Related transactions', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notes', + 'recurring_meta_field_bill_id' => 'Bill', + 'recurring_meta_field_piggy_bank_id' => 'Piggy bank', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/es_ES/form.php b/resources/lang/es_ES/form.php index 11d94d8b7f..b2162ec655 100644 --- a/resources/lang/es_ES/form.php +++ b/resources/lang/es_ES/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Banco', - 'bank_balance' => 'Saldo', - 'savings_balance' => 'Salgo de ahorro', - 'credit_card_limit' => 'Límite de la tarjeta de crédito', - 'automatch' => 'Coinciden automáticamente', - 'skip' => 'Saltar', - 'name' => 'Nombre', - 'active' => 'Activo', - 'amount_min' => 'Importe mínimo', - 'amount_max' => 'Importe máximo', - 'match' => 'Encuentros en', - 'strict' => 'Strict mode', - 'repeat_freq' => 'Repetición', - 'journal_currency_id' => 'Divisa', - 'currency_id' => 'Divisa', - 'transaction_currency_id' => 'Currency', - 'external_ip' => 'Your server\'s external IP', - 'attachments' => 'Adjuntos', - 'journal_amount' => 'Importe', - 'journal_source_account_name' => 'Cuenta de ingresos (origen)', - 'journal_source_account_id' => 'Cuenta de activos (origen)', - 'BIC' => 'BIC', - 'verify_password' => 'Verificar la seguridad de contraseña', - 'source_account' => 'Cuenta origen', - 'destination_account' => 'Cuenta destino', - 'journal_destination_account_id' => 'Cuenta de activos (destino)', - 'asset_destination_account' => 'Cuenta de activos (destino)', - 'asset_source_account' => 'Cuenta de activos (origen)', - 'journal_description' => 'Descripción', - 'note' => 'Notas', - 'split_journal' => 'Dividir esta transacción', - 'split_journal_explanation' => 'Dividir esta transacción en múltiples partes', - 'currency' => 'Divisa', - 'account_id' => 'Cuenta', - 'budget_id' => 'Presupuesto', - 'openingBalance' => 'Saldo inicial', - 'tagMode' => 'Modo de etiqueta', - 'tag_position' => 'Etiquetar ubicación', - 'virtualBalance' => 'Saldo virtual', - 'targetamount' => 'Cantidad objetivo', - 'accountRole' => 'Tipo de cuenta', - 'openingBalanceDate' => 'Fecha del saldo inicial', - 'ccType' => 'Plan de pagos con tarjeta de crédito', - 'ccMonthlyPaymentDate' => 'Fecha de pago mensual de la tarjeta de crédito', - 'piggy_bank_id' => 'Hucha', - 'returnHere' => 'Volver aquí', - 'returnHereExplanation' => 'Después de guardar, vuelve aquí para crear otro.', - 'returnHereUpdateExplanation' => 'Después de actualizar, vuelve aquí.', - 'description' => 'Descripción', - 'expense_account' => 'Cuenta de gastos', - 'revenue_account' => 'Cuenta de ingresos', - 'decimal_places' => 'Lugares decimales', - 'exchange_rate_instruction' => 'Monedas extranjeras', - 'source_amount' => 'Importe (origen)', - 'destination_amount' => 'Importe (destino)', - 'native_amount' => 'Cantidad nativa', - 'new_email_address' => 'Nueva dirección de email', - 'verification' => 'Verificación', - 'api_key' => 'Clave de API', - 'remember_me' => 'Recordarme', + 'bank_name' => 'Banco', + 'bank_balance' => 'Saldo', + 'savings_balance' => 'Salgo de ahorro', + 'credit_card_limit' => 'Límite de la tarjeta de crédito', + 'automatch' => 'Coinciden automáticamente', + 'skip' => 'Saltar', + 'name' => 'Nombre', + 'active' => 'Activo', + 'amount_min' => 'Importe mínimo', + 'amount_max' => 'Importe máximo', + 'match' => 'Encuentros en', + 'strict' => 'Strict mode', + 'repeat_freq' => 'Repetición', + 'journal_currency_id' => 'Divisa', + 'currency_id' => 'Divisa', + 'transaction_currency_id' => 'Currency', + 'external_ip' => 'Your server\'s external IP', + 'attachments' => 'Adjuntos', + 'journal_amount' => 'Importe', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Verificar la seguridad de contraseña', + 'source_account' => 'Cuenta origen', + 'destination_account' => 'Cuenta destino', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Cuenta de activos (destino)', + 'asset_source_account' => 'Cuenta de activos (origen)', + 'journal_description' => 'Descripción', + 'note' => 'Notas', + 'split_journal' => 'Dividir esta transacción', + 'split_journal_explanation' => 'Dividir esta transacción en múltiples partes', + 'currency' => 'Divisa', + 'account_id' => 'Cuenta', + 'budget_id' => 'Presupuesto', + 'openingBalance' => 'Saldo inicial', + 'tagMode' => 'Modo de etiqueta', + 'tag_position' => 'Etiquetar ubicación', + 'virtualBalance' => 'Saldo virtual', + 'targetamount' => 'Cantidad objetivo', + 'accountRole' => 'Tipo de cuenta', + 'openingBalanceDate' => 'Fecha del saldo inicial', + 'ccType' => 'Plan de pagos con tarjeta de crédito', + 'ccMonthlyPaymentDate' => 'Fecha de pago mensual de la tarjeta de crédito', + 'piggy_bank_id' => 'Hucha', + 'returnHere' => 'Volver aquí', + 'returnHereExplanation' => 'Después de guardar, vuelve aquí para crear otro.', + 'returnHereUpdateExplanation' => 'Después de actualizar, vuelve aquí.', + 'description' => 'Descripción', + 'expense_account' => 'Cuenta de gastos', + 'revenue_account' => 'Cuenta de ingresos', + 'decimal_places' => 'Lugares decimales', + 'exchange_rate_instruction' => 'Monedas extranjeras', + 'source_amount' => 'Importe (origen)', + 'destination_amount' => 'Importe (destino)', + 'native_amount' => 'Cantidad nativa', + 'new_email_address' => 'Nueva dirección de email', + 'verification' => 'Verificación', + 'api_key' => 'Clave de API', + 'remember_me' => 'Recordarme', 'source_account_asset' => 'Cuenta de origen (cuenta de activos)', 'destination_account_expense' => 'Cuenta de destino (cuenta de gastos)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Convertir depósito', 'convert_Transfer' => 'Convertir transferencia', - 'amount' => 'Importe', - 'foreign_amount' => 'Foreign amount', - 'existing_attachments' => 'Existing attachments', - 'date' => 'Fecha', - 'interest_date' => 'Fecha de interés', - 'book_date' => 'Fecha de registro', - 'process_date' => 'Fecha de procesamiento', - 'category' => 'Categoría', - 'tags' => 'Etiquetas', - 'deletePermanently' => 'Borrar permanentemente', - 'cancel' => 'Cancelar', - 'targetdate' => 'Fecha tope', - 'startdate' => 'Fecha de inicio', - 'tag' => 'Etiqueta', - 'under' => 'Debajo', - 'symbol' => 'Símbolo', - 'code' => 'Código', - 'iban' => 'IBAN', - 'accountNumber' => 'Número de cuenta', - 'creditCardNumber' => 'Número de la tarjeta de crédito', - 'has_headers' => 'Encabezados', - 'date_format' => 'Formato de fecha', - 'specifix' => 'Banco- o archivo de soluciones especificas', - 'attachments[]' => 'Adjuntos', - 'store_new_withdrawal' => 'Guardar rueva retirada de efectivo', - 'store_new_deposit' => 'Guardar nuevo depósito', - 'store_new_transfer' => 'Guardar nueva transferencia', - 'add_new_withdrawal' => 'Añadir rueva retirada de efectivo', - 'add_new_deposit' => 'Añadir nuevo depósito', - 'add_new_transfer' => 'Añadir nueva transferencia', - 'title' => 'Título', - 'notes' => 'Notas', - 'filename' => 'Nombre de fichero', - 'mime' => 'Tipo Mime', - 'size' => 'Tamaño', - 'trigger' => 'Disparador', - 'stop_processing' => 'Detener el procesamiento', - 'start_date' => 'Inicio del rango', - 'end_date' => 'Final del rango', - 'export_start_range' => 'Inicio del rango de exportación', - 'export_end_range' => 'Fin del rango de exportación', - 'export_format' => 'Formato del archivo', - 'include_attachments' => 'Incluir archivos adjuntos subidos', - 'include_old_uploads' => 'Incluir datos importados', - 'accounts' => 'Exportar transacciones de estas cuentas', - 'delete_account' => 'Borrar cuenta ":name"', - 'delete_bill' => 'Eliminar factura ":name"', - 'delete_budget' => 'Eliminar presupuesto ":name"', - 'delete_category' => 'Eliminar categoría ":name"', - 'delete_currency' => 'Eliminar divisa ":name"', - 'delete_journal' => 'Eliminar la transacción con descripción ":description"', - 'delete_attachment' => 'Eliminar adjunto ":name"', - 'delete_rule' => 'Eliminar regla ":title"', - 'delete_rule_group' => 'Eliminar grupo de reglas ":title"', - 'delete_link_type' => 'Eliminar tipo de enlace ":name"', - 'delete_user' => 'Eliminar usuario ":email"', - 'user_areYouSure' => 'Si elimina usuario ":email", todo desaparecerá. No hay deshacer, recuperar ni nada. Si te eliminas, perderás el acceso a esta instancia de Firefly III.', - 'attachment_areYouSure' => '¿Seguro que quieres eliminar el archivo adjunto llamado "name"?', - 'account_areYouSure' => '¿Seguro que quieres eliminar la cuenta llamada ":name"?', - 'bill_areYouSure' => '¿Seguro que quieres eliminar la factura llamada ":name"?', - 'rule_areYouSure' => '¿Seguro que quieres eliminar la regla titulada ":title"?', - 'ruleGroup_areYouSure' => '¿Seguro que quieres eliminar el grupo de reglas titulado ":title"?', - 'budget_areYouSure' => '¿Seguro que quieres eliminar el presupuesto llamado ":name"?', - 'category_areYouSure' => '¿Seguro que quieres eliminar la categoría llamada ":name"?', - 'currency_areYouSure' => '¿Está seguro que desea eliminar la moneda denominada ":name"?', - 'piggyBank_areYouSure' => '¿Está seguro que desea eliminar la hucha llamada ":name"?', - 'journal_areYouSure' => '¿Estás seguro de que deseas eliminar la transacción descrita ":description"?', - 'mass_journal_are_you_sure' => '¿Usted esta seguro de querer eliminar estas transacciones?', - 'tag_areYouSure' => '¿Seguro que quieres eliminar la etiqueta ":tag"?', - 'journal_link_areYouSure' => '¿Seguro que quieres eliminar el vínculo entre :source y :destination?', - 'linkType_areYouSure' => '¿Estás seguro de que deseas eliminar el tipo de vínculo ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', - 'mass_make_selection' => 'Aún puede evitar que se eliminen elementos quitando la casilla de verificación.', - 'delete_all_permanently' => 'Eliminar selección permanentemente', - 'update_all_journals' => 'Actualiza estas transacciones', - 'also_delete_transactions' => 'La única transacción conectada a esta cuenta también se eliminará. | Todas las :count transacciones conectadas a esta cuenta también se eliminarán.', - 'also_delete_connections' => 'La única transacción vinculada con este tipo de enlace perderá esta conexión. | Todas las :count transacciones vinculadas con este tipo de enlace perderán su conexión.', - 'also_delete_rules' => 'La única regla conectada a este grupo de reglas también se eliminará. | Todas las :count reglas conectadas a este grupo de reglas también se eliminarán.', - 'also_delete_piggyBanks' => 'La única alcancía conectada a esta cuenta también se eliminará. | Todas las :count alcancías conectadas a esta cuenta también se eliminará.', - 'bill_keep_transactions' => 'La única transacción conectada a esta factura no se eliminará. | Todas las :count transacciones conectadas a esta factura evitarán la eliminación.', - 'budget_keep_transactions' => 'La única transacción conectada a este presupuesto no se eliminará. | Todas las :count transacciones conectadas a este presupuesto evitarán la eliminación.', - 'category_keep_transactions' => 'La única transacción conectada a esta categoría no se eliminará. | Todas las :count transacciones conectadas a esta categoría evitarán la eliminación.', - 'tag_keep_transactions' => 'La única transacción conectada a esta etiqueta no se eliminará. | Todas las :count transacciones conectadas a esta etiqueta evitarán la eliminación.', - 'check_for_updates' => 'Ver actualizaciones', + 'amount' => 'Importe', + 'foreign_amount' => 'Foreign amount', + 'existing_attachments' => 'Existing attachments', + 'date' => 'Fecha', + 'interest_date' => 'Fecha de interés', + 'book_date' => 'Fecha de registro', + 'process_date' => 'Fecha de procesamiento', + 'category' => 'Categoría', + 'tags' => 'Etiquetas', + 'deletePermanently' => 'Borrar permanentemente', + 'cancel' => 'Cancelar', + 'targetdate' => 'Fecha tope', + 'startdate' => 'Fecha de inicio', + 'tag' => 'Etiqueta', + 'under' => 'Debajo', + 'symbol' => 'Símbolo', + 'code' => 'Código', + 'iban' => 'IBAN', + 'accountNumber' => 'Número de cuenta', + 'creditCardNumber' => 'Número de la tarjeta de crédito', + 'has_headers' => 'Encabezados', + 'date_format' => 'Formato de fecha', + 'specifix' => 'Banco- o archivo de soluciones especificas', + 'attachments[]' => 'Adjuntos', + 'store_new_withdrawal' => 'Guardar rueva retirada de efectivo', + 'store_new_deposit' => 'Guardar nuevo depósito', + 'store_new_transfer' => 'Guardar nueva transferencia', + 'add_new_withdrawal' => 'Añadir rueva retirada de efectivo', + 'add_new_deposit' => 'Añadir nuevo depósito', + 'add_new_transfer' => 'Añadir nueva transferencia', + 'title' => 'Título', + 'notes' => 'Notas', + 'filename' => 'Nombre de fichero', + 'mime' => 'Tipo Mime', + 'size' => 'Tamaño', + 'trigger' => 'Disparador', + 'stop_processing' => 'Detener el procesamiento', + 'start_date' => 'Inicio del rango', + 'end_date' => 'Final del rango', + 'export_start_range' => 'Inicio del rango de exportación', + 'export_end_range' => 'Fin del rango de exportación', + 'export_format' => 'Formato del archivo', + 'include_attachments' => 'Incluir archivos adjuntos subidos', + 'include_old_uploads' => 'Incluir datos importados', + 'accounts' => 'Exportar transacciones de estas cuentas', + 'delete_account' => 'Borrar cuenta ":name"', + 'delete_bill' => 'Eliminar factura ":name"', + 'delete_budget' => 'Eliminar presupuesto ":name"', + 'delete_category' => 'Eliminar categoría ":name"', + 'delete_currency' => 'Eliminar divisa ":name"', + 'delete_journal' => 'Eliminar la transacción con descripción ":description"', + 'delete_attachment' => 'Eliminar adjunto ":name"', + 'delete_rule' => 'Eliminar regla ":title"', + 'delete_rule_group' => 'Eliminar grupo de reglas ":title"', + 'delete_link_type' => 'Eliminar tipo de enlace ":name"', + 'delete_user' => 'Eliminar usuario ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Si elimina usuario ":email", todo desaparecerá. No hay deshacer, recuperar ni nada. Si te eliminas, perderás el acceso a esta instancia de Firefly III.', + 'attachment_areYouSure' => '¿Seguro que quieres eliminar el archivo adjunto llamado "name"?', + 'account_areYouSure' => '¿Seguro que quieres eliminar la cuenta llamada ":name"?', + 'bill_areYouSure' => '¿Seguro que quieres eliminar la factura llamada ":name"?', + 'rule_areYouSure' => '¿Seguro que quieres eliminar la regla titulada ":title"?', + 'ruleGroup_areYouSure' => '¿Seguro que quieres eliminar el grupo de reglas titulado ":title"?', + 'budget_areYouSure' => '¿Seguro que quieres eliminar el presupuesto llamado ":name"?', + 'category_areYouSure' => '¿Seguro que quieres eliminar la categoría llamada ":name"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => '¿Está seguro que desea eliminar la moneda denominada ":name"?', + 'piggyBank_areYouSure' => '¿Está seguro que desea eliminar la hucha llamada ":name"?', + 'journal_areYouSure' => '¿Estás seguro de que deseas eliminar la transacción descrita ":description"?', + 'mass_journal_are_you_sure' => '¿Usted esta seguro de querer eliminar estas transacciones?', + 'tag_areYouSure' => '¿Seguro que quieres eliminar la etiqueta ":tag"?', + 'journal_link_areYouSure' => '¿Seguro que quieres eliminar el vínculo entre :source y :destination?', + 'linkType_areYouSure' => '¿Estás seguro de que deseas eliminar el tipo de vínculo ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', + 'mass_make_selection' => 'Aún puede evitar que se eliminen elementos quitando la casilla de verificación.', + 'delete_all_permanently' => 'Eliminar selección permanentemente', + 'update_all_journals' => 'Actualiza estas transacciones', + 'also_delete_transactions' => 'La única transacción conectada a esta cuenta también se eliminará. | Todas las :count transacciones conectadas a esta cuenta también se eliminarán.', + 'also_delete_connections' => 'La única transacción vinculada con este tipo de enlace perderá esta conexión. | Todas las :count transacciones vinculadas con este tipo de enlace perderán su conexión.', + 'also_delete_rules' => 'La única regla conectada a este grupo de reglas también se eliminará. | Todas las :count reglas conectadas a este grupo de reglas también se eliminarán.', + 'also_delete_piggyBanks' => 'La única alcancía conectada a esta cuenta también se eliminará. | Todas las :count alcancías conectadas a esta cuenta también se eliminará.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Ver actualizaciones', 'email' => 'Correo electrónico', 'password' => 'Contraseña', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Código del país', 'provider_code' => 'Banco o proveedor de datos', - 'due_date' => 'Fecha de vencimiento', - 'payment_date' => 'Fecha de pago', - 'invoice_date' => 'Fecha de la factura', - 'internal_reference' => 'Referencia interna', - 'inward' => 'Descripción interna', - 'outward' => 'Descripción externa', - 'rule_group_id' => 'Grupo de reglas', + 'due_date' => 'Fecha de vencimiento', + 'payment_date' => 'Fecha de pago', + 'invoice_date' => 'Fecha de la factura', + 'internal_reference' => 'Referencia interna', + 'inward' => 'Descripción interna', + 'outward' => 'Descripción externa', + 'rule_group_id' => 'Grupo de reglas', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/es_ES/import.php b/resources/lang/es_ES/import.php index 8a812e4689..3676742295 100644 --- a/resources/lang/es_ES/import.php +++ b/resources/lang/es_ES/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/es_ES/list.php b/resources/lang/es_ES/list.php index 2aa21df77b..ea67eabb8a 100644 --- a/resources/lang/es_ES/list.php +++ b/resources/lang/es_ES/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Last login', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq payment ID', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transaction(s)', + 'field' => 'Field', + 'value' => 'Value', ]; diff --git a/resources/lang/es_ES/validation.php b/resources/lang/es_ES/validation.php index 227ebdc572..106e5f668c 100644 --- a/resources/lang/es_ES/validation.php +++ b/resources/lang/es_ES/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Este no es un IBAN válido.', - 'source_equals_destination' => 'The source account equals the destination account', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Parece que este número de cuenta ya está en uso.', 'unique_iban_for_user' => 'It looks like this IBAN is already in use.', 'deleted_user' => 'Debido a restricciones de seguridad, no se puede registrar utilizando esta dirección de correo electrónico.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Archivo cargado con exito ":name".', 'must_exist' => 'ID introducido en :attribute el atributo no existe en la base de datos.', 'all_accounts_equal' => 'Todas las cuentas en este campo deben ser iguales.', - 'invalid_selection' => 'Your selection is invalid', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'Este valor no es válido para este campo.', 'at_least_one_transaction' => 'Se necesita al menos una transacción.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'El contenido de este campo no es válido sin la información montearia.', 'equal_description' => 'Transaction description should not equal global description.', 'file_invalid_mime' => 'El archivo ":name" es de tipo ":mime", el cual no se acepta.', 'file_too_large' => 'El archivo ":name" es demasiado grande.', - 'belongs_to_user' => 'El valor de :attribute es desconocido', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => 'El :attribute debe ser aceptado.', 'bic' => 'Esto no es un BIC válido.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute debe ser mayor que cero.', 'active_url' => 'El campo :attribute no es una URL válida.', 'after' => 'El campo :attribute debe ser una fecha posterior a :date.', @@ -53,8 +59,8 @@ return [ 'array' => 'El campo :attribute debe ser un arreglo.', 'unique_for_user' => 'Ya hay una entrada con esto :attribute.', 'before' => 'El campo :attribute debe contener una fecha anterior a :date.', - 'unique_object_for_user' => 'Este nombre ya está en uso', - 'unique_account_for_user' => 'Este nombre cuenta ya está en uso', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => 'El atributo :attribute debe estar entre :min y :max.', 'between.file' => 'El atributo :attribute debe estar entre :min y :max kilobytes.', 'between.string' => 'El atributo :attribute debe estar entre :min y :max caracteres.', @@ -85,6 +91,9 @@ return [ 'min.array' => 'El campo :attribute debe tener al menos :min elementos.', 'not_in' => 'El campo :attribute seleccionado es incorrecto.', 'numeric' => 'El campo :attribute debe ser un número.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'El formato del campo :attribute no es válido.', 'required' => 'El campo :attribute es obligatorio.', 'required_if' => 'El campo :attribute es obligatorio cuando el campo :other es :value.', @@ -109,9 +118,12 @@ return [ 'file' => 'El campo :attribute debe ser un fichero.', 'in_array' => 'El campo :attribute no existe en :other.', 'present' => 'El campo :attribute debe estar presente.', - 'amount_zero' => 'La cantidad total no puede ser cero', + 'amount_zero' => 'The total amount cannot be zero.', 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'dirección de correo electrónico', 'description' => 'descripcion', diff --git a/resources/lang/fr_FR/auth.php b/resources/lang/fr_FR/auth.php index 689b6966af..974345567e 100644 --- a/resources/lang/fr_FR/auth.php +++ b/resources/lang/fr_FR/auth.php @@ -24,5 +24,5 @@ declare(strict_types=1); return [ 'failed' => 'Ces identifiants n\'ont aucune correspondance.', - 'throttle' => 'Trop de tentatives de connexion. Veuillez essayer à nouveau dans :seconds secondes.', + 'throttle' => 'Trop de tentatives de connexion. Veuillez réessayer dans :seconds secondes.', ]; diff --git a/resources/lang/fr_FR/config.php b/resources/lang/fr_FR/config.php index 763721bdba..05283eb88f 100644 --- a/resources/lang/fr_FR/config.php +++ b/resources/lang/fr_FR/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'fr', - 'locale' => 'fr, French, fr_FR, fr_FR.utf8, fr_FR.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%B %e %Y @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Semaine %W %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'fr', + 'locale' => 'fr, French, fr_FR, fr_FR.utf8, fr_FR.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%B %e %Y @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Semaine %W %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'Do MMMM YYYY', + 'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Monday', + 'dow_2' => 'Tuesday', + 'dow_3' => 'Wednesday', + 'dow_4' => 'Thursday', + 'dow_5' => 'Friday', + 'dow_6' => 'Saturday', + 'dow_7' => 'Sunday', ]; diff --git a/resources/lang/fr_FR/demo.php b/resources/lang/fr_FR/demo.php index 94171d0aa6..6464f29911 100644 --- a/resources/lang/fr_FR/demo.php +++ b/resources/lang/fr_FR/demo.php @@ -33,5 +33,7 @@ return [ 'currencies-index' => 'Firefly III prend en charge plusieurs devises. Bien que l\'Euro soit la devise par défaut, cette dernière peut être changée pour le Dollar américain et de nombreuses autres devises. Comme vous pouvez le remarquer une petite sélection des monnaies a été incluse, mais vous pouvez ajouter vos propres devises si vous le souhaitez. Gardez à l\'esprit que la modification de la devise par défaut ne modifie pas la monnaie des transactions existantes : Firefly III prend en charge l’utilisation de plusieurs devises en même temps.', 'transactions-index' => 'Ces dépenses, dépôts et transferts ne sont pas particulièrement imaginatifs. Ils ont été générés automatiquement.', 'piggy-banks-index' => 'Comme vous pouvez le voir, il y a trois tirelires. Utilisez les boutons plus et moins pour influer sur le montant d’argent dans chaque tirelire. Cliquez sur le nom de la tirelire pour voir l’administration pour chaque tirelire.', - 'import-index' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'import-index' => 'Tout fichier CSV peut être importé dans Firefly III. L\'importation de données depuis bunq et Specter est également prise en charge. D\'autres banques et agrégateurs financiers seront mis en place dans le futur. En tant qu\'utilisateur du site de démonstration, vous ne pouvez voir que le «faux» service en action. Il va générer des transactions aléatoires pour vous montrer comment se déroule le processus.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index 9366f83e3d..f082d0631f 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Scanner le code QR avec une application sur votre téléphone comme Authy ou Google Authenticator et entrez le code généré.', 'pref_two_factor_auth_reset_code' => 'Réinitialiser le code de vérification', 'pref_two_factor_auth_disable_2fa' => 'Désactiver l\'authentification en deux étapes', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Enregistrer les paramètres', 'saved_preferences' => 'Préférences enregistrées!', 'preferences_general' => 'Général', @@ -820,7 +821,7 @@ return [ 'language' => 'Langage', 'new_savings_account' => ':bank_name compte d\'épargne', 'cash_wallet' => 'Porte-monnaie', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'Si la devise que vous utilisez habituellement n\'est pas répertoriée, ne vous inquiétez pas. Vous pouvez créer vos propres devises dans Options > Devises.', // home page: 'yourAccounts' => 'Vos comptes', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Solde à la fin de la période', 'splitByAccount' => 'Divisé par compte', 'coveredWithTags' => 'Recouvert de tags', - 'leftUnbalanced' => 'Restant déséquilibré', 'leftInBudget' => 'Budget restant', 'sumOfSums' => 'Montant des sommes', 'noCategory' => '(aucune catégorie)', @@ -1018,7 +1018,7 @@ return [ 'remove_money_from_piggy_title' => 'Retirer l’argent de la tirelire ":name"', 'add' => 'Ajouter', 'no_money_for_piggy' => 'Vous n\'avez pas d\'argent à placer dans cette tirelire.', - 'suggested_savings_per_month' => 'Suggested per month', + 'suggested_savings_per_month' => 'Suggéré par mois', 'remove' => 'Enlever', 'max_amount_add' => 'Le montant maximum que vous pouvez ajouter est', @@ -1062,7 +1062,7 @@ return [ 'instance_configuration' => 'Configuration', 'firefly_instance_configuration' => 'Options de configuration pour Firefly III', 'setting_single_user_mode' => 'Mode utilisateur unique', - 'setting_single_user_mode_explain' => 'Par défaut, Firefly III accepte uniquement un (1) enregistrement : vous. Il s\'agit d\'une mesure de sécurité qui empêche les autres d\'utiliser votre instance, à moins que vous ne les autorisiez. Les enregistrements futurs sont bloqués. Lorsque vous désactivez cette case, d\'autres personnes peuvent utiliser votre instance aussi bien, en supposant qu\'elles puissent l\'atteindre (quand il est connecté à Internet).', + '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 well, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Sauvegarder la configuration', 'single_user_administration' => 'Gestion de l\'utilisateur pour :email', 'edit_user' => 'Modifier l\'utilisateur :email', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => 'est (partiellement) remboursé par', 'is (partially) paid for by_inward' => 'est (partiellement) payé par', 'is (partially) reimbursed by_inward' => 'est (partiellement) remboursé par', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'se rapporte à', '(partially) refunds_outward' => 'rembourse (partiellement)', '(partially) pays for_outward' => 'paye (partiellement) pour', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Vous ne pouvez pas convertir une transaction ventilée', // Import page (general strings only) - 'import_index_title' => 'Importer des données dans Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Importer des données', + 'import_transactions' => 'Import transactions', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Cette fonction n\'est pas disponible lorsque vous utilisez Firefly III dans un environnement Sandstorm.io.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Vous n\'avez pas encore de factures. Vous pouvez créer des factures pour suivre les dépenses ordinaires, comme votre loyer ou l\'assurance.', 'no_bills_imperative_default' => 'Avez-vous des factures régulières ? Créez une facture et suivez vos paiements :', 'no_bills_create_default' => 'Créer une facture', + + // recurring transactions + 'recurrences' => 'Recurring transactions', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Create a recurring transaction', + 'make_new_recurring' => 'Create a recurring transaction', + 'recurring_daily' => 'Every day', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Related transactions', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notes', + 'recurring_meta_field_bill_id' => 'Bill', + 'recurring_meta_field_piggy_bank_id' => 'Piggy bank', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/fr_FR/form.php b/resources/lang/fr_FR/form.php index 5c57f4717a..574c59bd45 100644 --- a/resources/lang/fr_FR/form.php +++ b/resources/lang/fr_FR/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Nom de la banque', - 'bank_balance' => 'Solde', - 'savings_balance' => 'Solde de l\'épargne', - 'credit_card_limit' => 'Limite de carte de crédit', - 'automatch' => 'Correspondre automatiquement', - 'skip' => 'Ignorer', - 'name' => 'Nom', - 'active' => 'Actif', - 'amount_min' => 'Montant minimum', - 'amount_max' => 'Montant maximum', - 'match' => 'Correspondre à', - 'strict' => 'Strict mode', - 'repeat_freq' => 'Répétitions', - 'journal_currency_id' => 'Devise', - 'currency_id' => 'Devise', - 'transaction_currency_id' => 'Currency', - 'external_ip' => 'Your server\'s external IP', - 'attachments' => 'Documents joints', - 'journal_amount' => 'Montant', - 'journal_source_account_name' => 'Compte de recettes (source)', - 'journal_source_account_id' => 'Compte d’actif (source)', - 'BIC' => 'Code BIC', - 'verify_password' => 'Vérifiez la sécurité du mot de passe', - 'source_account' => 'Compte d\'origine', - 'destination_account' => 'Compte destinataire', - 'journal_destination_account_id' => 'Compte d’actif (destination)', - 'asset_destination_account' => 'Compte d’actif (destination)', - 'asset_source_account' => 'Compte d’actif (source)', - 'journal_description' => 'Description', - 'note' => 'Notes', - 'split_journal' => 'Ventiler cette opération', - 'split_journal_explanation' => 'Diviser cette opération en plusieurs parties', - 'currency' => 'Devise', - 'account_id' => 'Compte d’actif', - 'budget_id' => 'Budget', - 'openingBalance' => 'Solde initial', - 'tagMode' => 'Mode tag', - 'tag_position' => 'Localisation de l\'étiquette', - 'virtualBalance' => 'Solde virtuel', - 'targetamount' => 'Montant cible', - 'accountRole' => 'Rôle du compte', - 'openingBalanceDate' => 'Date du solde initial', - 'ccType' => 'Plan de paiement de carte de crédit', - 'ccMonthlyPaymentDate' => 'Date de paiement mensuelle de carte de crédit', - 'piggy_bank_id' => 'Tirelire', - 'returnHere' => 'Retourner ici', - 'returnHereExplanation' => 'Après enregistrement, revenir ici pour en créer un nouveau.', - 'returnHereUpdateExplanation' => 'Après mise à jour, revenir ici.', - 'description' => 'Description', - 'expense_account' => 'Compte de dépenses', - 'revenue_account' => 'Compte de recettes', - 'decimal_places' => 'Chiffres après la virgule', - 'exchange_rate_instruction' => 'Devises étrangères', - 'source_amount' => 'Montant (source)', - 'destination_amount' => 'Montant (destination)', - 'native_amount' => 'Montant natif', - 'new_email_address' => 'Nouvelle adresse email', - 'verification' => 'Vérification', - 'api_key' => 'Clé API', - 'remember_me' => 'Se souvenir de moi', + 'bank_name' => 'Nom de la banque', + 'bank_balance' => 'Solde', + 'savings_balance' => 'Solde de l\'épargne', + 'credit_card_limit' => 'Limite de carte de crédit', + 'automatch' => 'Correspondre automatiquement', + 'skip' => 'Ignorer', + 'name' => 'Nom', + 'active' => 'Actif', + 'amount_min' => 'Montant minimum', + 'amount_max' => 'Montant maximum', + 'match' => 'Correspondre à', + 'strict' => 'Mode strict', + 'repeat_freq' => 'Répétitions', + 'journal_currency_id' => 'Devise', + 'currency_id' => 'Devise', + 'transaction_currency_id' => 'Devise', + 'external_ip' => 'L\'adresse IP externe de votre serveur', + 'attachments' => 'Documents joints', + 'journal_amount' => 'Montant', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'Code BIC', + 'verify_password' => 'Vérifiez la sécurité du mot de passe', + 'source_account' => 'Compte d\'origine', + 'destination_account' => 'Compte destinataire', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Compte d’actif (destination)', + 'asset_source_account' => 'Compte d’actif (source)', + 'journal_description' => 'Description', + 'note' => 'Notes', + 'split_journal' => 'Ventiler cette opération', + 'split_journal_explanation' => 'Diviser cette opération en plusieurs parties', + 'currency' => 'Devise', + 'account_id' => 'Compte d’actif', + 'budget_id' => 'Budget', + 'openingBalance' => 'Solde initial', + 'tagMode' => 'Mode tag', + 'tag_position' => 'Localisation de l\'étiquette', + 'virtualBalance' => 'Solde virtuel', + 'targetamount' => 'Montant cible', + 'accountRole' => 'Rôle du compte', + 'openingBalanceDate' => 'Date du solde initial', + 'ccType' => 'Plan de paiement de carte de crédit', + 'ccMonthlyPaymentDate' => 'Date de paiement mensuelle de carte de crédit', + 'piggy_bank_id' => 'Tirelire', + 'returnHere' => 'Retourner ici', + 'returnHereExplanation' => 'Après enregistrement, revenir ici pour en créer un nouveau.', + 'returnHereUpdateExplanation' => 'Après mise à jour, revenir ici.', + 'description' => 'Description', + 'expense_account' => 'Compte de dépenses', + 'revenue_account' => 'Compte de recettes', + 'decimal_places' => 'Chiffres après la virgule', + 'exchange_rate_instruction' => 'Devises étrangères', + 'source_amount' => 'Montant (source)', + 'destination_amount' => 'Montant (destination)', + 'native_amount' => 'Montant natif', + 'new_email_address' => 'Nouvelle adresse email', + 'verification' => 'Vérification', + 'api_key' => 'Clé API', + 'remember_me' => 'Se souvenir de moi', 'source_account_asset' => 'Compte source (compte d\'actif)', 'destination_account_expense' => 'Compte de destination (compte de dépenses)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Convertir le dépôt', 'convert_Transfer' => 'Convertir le transfert', - 'amount' => 'Montant', - 'foreign_amount' => 'Foreign amount', - 'existing_attachments' => 'Existing attachments', - 'date' => 'Date', - 'interest_date' => 'Date de l’intérêt', - 'book_date' => 'Date de réservation', - 'process_date' => 'Date de traitement', - 'category' => 'Catégorie', - 'tags' => 'Mots-clés', - 'deletePermanently' => 'Supprimer définitivement', - 'cancel' => 'Annuler', - 'targetdate' => 'Date cible', - 'startdate' => 'Date de début', - 'tag' => 'Mot-clé', - 'under' => 'En dessous de', - 'symbol' => 'Symbole', - 'code' => 'Code', - 'iban' => 'Numéro IBAN', - 'accountNumber' => 'N° de compte', - 'creditCardNumber' => 'Numéro de carte de crédit', - 'has_headers' => 'Entêtes ', - 'date_format' => 'Format de la date', - 'specifix' => 'Banque - ou déposer des corrections spécifiques', - 'attachments[]' => 'Pièces jointes', - 'store_new_withdrawal' => 'Enregistrer un nouveau retrait', - 'store_new_deposit' => 'Enregistrer un nouveau dépôt', - 'store_new_transfer' => 'Enregistrer un nouveau transfert', - 'add_new_withdrawal' => 'Ajouter un nouveau retrait', - 'add_new_deposit' => 'Ajouter un nouveau dépôt', - 'add_new_transfer' => 'Ajouter un nouveau transfert', - 'title' => 'Titre', - 'notes' => 'Notes', - 'filename' => 'Nom du fichier', - 'mime' => 'Type Mime', - 'size' => 'Taille', - 'trigger' => 'Déclencheur', - 'stop_processing' => 'Arrêter le traitement', - 'start_date' => 'Début de l\'étendue', - 'end_date' => 'Fin de l\'étendue', - 'export_start_range' => 'Début de l’étendue d’exportation', - 'export_end_range' => 'Fin de l’étendue d\'exportation', - 'export_format' => 'Format de fichier', - 'include_attachments' => 'Inclure des pièces jointes téléchargées', - 'include_old_uploads' => 'Inclure les données importées', - 'accounts' => 'Exporter les opérations depuis ces comptes', - 'delete_account' => 'Supprimer le compte ":name"', - 'delete_bill' => 'Supprimer la facture ":name"', - 'delete_budget' => 'Supprimer le budget ":name"', - 'delete_category' => 'Supprimer la catégorie ":name"', - 'delete_currency' => 'Supprimer la devise ":name"', - 'delete_journal' => 'Supprimer l\'opération ayant comme description ":description"', - 'delete_attachment' => 'Supprimer la pièce jointe ":name"', - 'delete_rule' => 'Supprimer la règle ":title"', - 'delete_rule_group' => 'Supprimer le groupe de filtres ":title"', - 'delete_link_type' => 'Supprimer le type de lien ":name"', - 'delete_user' => 'Supprimer l\'utilisateur ":email"', - 'user_areYouSure' => 'Si vous supprimez l\'utilisateur ":email", tout disparaitra. Il n\'y a pas d\'annulation, de "dé-suppression" ou quoi que ce soit de la sorte. Si vous supprimez votre propre compte, vous n\'aurez plus accès à cette instance de Firefly III.', - 'attachment_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la pièce jointe nommée ":name" ?', - 'account_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le compte nommé ":name" ?', - 'bill_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la facture nommée ":name" ?', - 'rule_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la règle intitulée ":title" ?', - 'ruleGroup_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le groupe de règles intitulé ":title" ?', - 'budget_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le budget nommé ":name" ?', - 'category_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la catégorie nommée ":name" ?', - 'currency_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la devise nommée ":name" ?', - 'piggyBank_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la tirelire nommée ":name" ?', - 'journal_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la description de l\'opération ":description" ?', - 'mass_journal_are_you_sure' => 'Êtes-vous sûr de que vouloir supprimer ces opérations ?', - 'tag_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le tag ":tag" ?', - 'journal_link_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le lien entre :source et :destination?', - 'linkType_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le type de lien ":name" (":inward" / ":outward") ?', - 'permDeleteWarning' => 'Supprimer quelque chose dans Firefly est permanent et ne peut pas être annulé.', - 'mass_make_selection' => 'Vous pouvez toujours empêcher des éléments d’être supprimés en décochant la case à cocher.', - 'delete_all_permanently' => 'Supprimer la selection définitivement', - 'update_all_journals' => 'Mettre à jour ces opérations', - 'also_delete_transactions' => 'La seule opération liée à ce compte sera aussi supprimée.|Les :count opérations liées à ce compte seront aussi supprimées.', - 'also_delete_connections' => 'La seule transaction liée à ce type de lien perdra cette connexion. | Toutes les transactions :count liées à ce type de lien perdront leur connexion.', - 'also_delete_rules' => 'La seule règle liée à ce groupe de règles sera aussi supprimée.|Les :count règles liées à ce groupe de règles seront aussi supprimées.', - 'also_delete_piggyBanks' => 'La seule tirelire liée à ce compte sera aussi supprimée.|Les :count tirelires liées à ce compte seront aussi supprimées.', - 'bill_keep_transactions' => 'La seule opération liée à cette facture ne sera pas supprimée.|Les :count opérations liées à cette facture ne seront pas supprimées.', - 'budget_keep_transactions' => 'La seule opération liée à ce budget ne sera pas supprimée.|Les :count opérations liées à ce budget ne seront pas supprimées.', - 'category_keep_transactions' => 'La seule opération liée à cette catégorie ne sera pas supprimée.|Les :count opérations liées à cette catégorie ne seront pas supprimées.', - 'tag_keep_transactions' => 'La seule opération liée à ce tag ne sera pas supprimée.|Les :count opérations liées à ce tag ne seront pas supprimées.', - 'check_for_updates' => 'Vérifier les mises à jour', + 'amount' => 'Montant', + 'foreign_amount' => 'Montant externe', + 'existing_attachments' => 'Pièces jointes existantes', + 'date' => 'Date', + 'interest_date' => 'Date de l’intérêt', + 'book_date' => 'Date de réservation', + 'process_date' => 'Date de traitement', + 'category' => 'Catégorie', + 'tags' => 'Mots-clés', + 'deletePermanently' => 'Supprimer définitivement', + 'cancel' => 'Annuler', + 'targetdate' => 'Date cible', + 'startdate' => 'Date de début', + 'tag' => 'Mot-clé', + 'under' => 'En dessous de', + 'symbol' => 'Symbole', + 'code' => 'Code', + 'iban' => 'Numéro IBAN', + 'accountNumber' => 'N° de compte', + 'creditCardNumber' => 'Numéro de carte de crédit', + 'has_headers' => 'Entêtes ', + 'date_format' => 'Format de la date', + 'specifix' => 'Banque - ou déposer des corrections spécifiques', + 'attachments[]' => 'Pièces jointes', + 'store_new_withdrawal' => 'Enregistrer un nouveau retrait', + 'store_new_deposit' => 'Enregistrer un nouveau dépôt', + 'store_new_transfer' => 'Enregistrer un nouveau transfert', + 'add_new_withdrawal' => 'Ajouter un nouveau retrait', + 'add_new_deposit' => 'Ajouter un nouveau dépôt', + 'add_new_transfer' => 'Ajouter un nouveau transfert', + 'title' => 'Titre', + 'notes' => 'Notes', + 'filename' => 'Nom du fichier', + 'mime' => 'Type Mime', + 'size' => 'Taille', + 'trigger' => 'Déclencheur', + 'stop_processing' => 'Arrêter le traitement', + 'start_date' => 'Début de l\'étendue', + 'end_date' => 'Fin de l\'étendue', + 'export_start_range' => 'Début de l’étendue d’exportation', + 'export_end_range' => 'Fin de l’étendue d\'exportation', + 'export_format' => 'Format de fichier', + 'include_attachments' => 'Inclure des pièces jointes téléchargées', + 'include_old_uploads' => 'Inclure les données importées', + 'accounts' => 'Exporter les opérations depuis ces comptes', + 'delete_account' => 'Supprimer le compte ":name"', + 'delete_bill' => 'Supprimer la facture ":name"', + 'delete_budget' => 'Supprimer le budget ":name"', + 'delete_category' => 'Supprimer la catégorie ":name"', + 'delete_currency' => 'Supprimer la devise ":name"', + 'delete_journal' => 'Supprimer l\'opération ayant comme description ":description"', + 'delete_attachment' => 'Supprimer la pièce jointe ":name"', + 'delete_rule' => 'Supprimer la règle ":title"', + 'delete_rule_group' => 'Supprimer le groupe de filtres ":title"', + 'delete_link_type' => 'Supprimer le type de lien ":name"', + 'delete_user' => 'Supprimer l\'utilisateur ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Si vous supprimez l\'utilisateur ":email", tout disparaitra. Il n\'y a pas d\'annulation, de "dé-suppression" ou quoi que ce soit de la sorte. Si vous supprimez votre propre compte, vous n\'aurez plus accès à cette instance de Firefly III.', + 'attachment_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la pièce jointe nommée ":name" ?', + 'account_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le compte nommé ":name" ?', + 'bill_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la facture nommée ":name" ?', + 'rule_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la règle intitulée ":title" ?', + 'ruleGroup_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le groupe de règles intitulé ":title" ?', + 'budget_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le budget nommé ":name" ?', + 'category_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la catégorie nommée ":name" ?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la devise nommée ":name" ?', + 'piggyBank_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la tirelire nommée ":name" ?', + 'journal_areYouSure' => 'Êtes-vous sûr de vouloir supprimer la description de l\'opération ":description" ?', + 'mass_journal_are_you_sure' => 'Êtes-vous sûr de que vouloir supprimer ces opérations ?', + 'tag_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le tag ":tag" ?', + 'journal_link_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le lien entre :source et :destination?', + 'linkType_areYouSure' => 'Êtes-vous sûr de vouloir supprimer le type de lien ":name" (":inward" / ":outward") ?', + 'permDeleteWarning' => 'Supprimer quelque chose dans Firefly est permanent et ne peut pas être annulé.', + 'mass_make_selection' => 'Vous pouvez toujours empêcher des éléments d’être supprimés en décochant la case à cocher.', + 'delete_all_permanently' => 'Supprimer la selection définitivement', + 'update_all_journals' => 'Mettre à jour ces opérations', + 'also_delete_transactions' => 'La seule opération liée à ce compte sera aussi supprimée.|Les :count opérations liées à ce compte seront aussi supprimées.', + 'also_delete_connections' => 'La seule transaction liée à ce type de lien perdra cette connexion. | Toutes les transactions :count liées à ce type de lien perdront leur connexion.', + 'also_delete_rules' => 'La seule règle liée à ce groupe de règles sera aussi supprimée.|Les :count règles liées à ce groupe de règles seront aussi supprimées.', + 'also_delete_piggyBanks' => 'La seule tirelire liée à ce compte sera aussi supprimée.|Les :count tirelires liées à ce compte seront aussi supprimées.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Vérifier les mises à jour', 'email' => 'Adresse Email', 'password' => 'Mot de passe', @@ -186,10 +189,10 @@ return [ 'blocked_code' => 'Raison du blocage', // import - 'apply_rules' => 'Apply rules', - 'artist' => 'Artist', + 'apply_rules' => 'Appliquer les règles', + 'artist' => 'Artiste', 'album' => 'Album', - 'song' => 'Song', + 'song' => 'Titre', // admin @@ -210,17 +213,29 @@ return [ 'client_id' => 'Identifiant', 'service_secret' => 'Secret de service', 'app_secret' => 'Secret d\'application', - 'app_id' => 'App ID', + 'app_id' => 'ID App', 'secret' => 'Secret', 'public_key' => 'Clé publique', 'country_code' => 'Code pays', 'provider_code' => 'Banque ou fournisseur de données', - 'due_date' => 'Échéance', - 'payment_date' => 'Date de paiement', - 'invoice_date' => 'Date de facturation', - 'internal_reference' => 'Référence interne', - 'inward' => 'Description vers l’intérieur', - 'outward' => 'Description de l’extérieur', - 'rule_group_id' => 'Groupe de règles', + 'due_date' => 'Échéance', + 'payment_date' => 'Date de paiement', + 'invoice_date' => 'Date de facturation', + 'internal_reference' => 'Référence interne', + 'inward' => 'Description vers l’intérieur', + 'outward' => 'Description de l’extérieur', + 'rule_group_id' => 'Groupe de règles', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/fr_FR/import.php b/resources/lang/fr_FR/import.php index a111bb834e..609ca1d477 100644 --- a/resources/lang/fr_FR/import.php +++ b/resources/lang/fr_FR/import.php @@ -24,33 +24,33 @@ declare(strict_types=1); return [ // ALL breadcrumbs and subtitles: - 'index_breadcrumb' => 'Import data into Firefly III', - 'prerequisites_breadcrumb_fake' => 'Prerequisites for the fake import provider', - 'prerequisites_breadcrumb_spectre' => 'Prerequisites for Spectre', - 'prerequisites_breadcrumb_bunq' => 'Prerequisites for bunq', - 'job_configuration_breadcrumb' => 'Configuration for ":key"', - 'job_status_breadcrumb' => 'Import status for ":key"', - 'cannot_create_for_provider' => 'Firefly III cannot create a job for the ":provider"-provider.', + 'index_breadcrumb' => 'Importer des données dans Firefly III', + 'prerequisites_breadcrumb_fake' => 'Prérequis pour la simulation d\'importation', + 'prerequisites_breadcrumb_spectre' => 'Prérequis pour Spectre', + 'prerequisites_breadcrumb_bunq' => 'Prérequis pour bunq', + 'job_configuration_breadcrumb' => 'Configuration pour ":key"', + 'job_status_breadcrumb' => 'Statut d\'importation pour ":key"', + 'cannot_create_for_provider' => 'Firefly III ne peut pas créer de tâche pour le fournisseur ":provider".', // index page: - 'general_index_title' => 'Import a file', - 'general_index_intro' => 'Welcome to Firefly III\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', + 'general_index_title' => 'Importer un fichier', + 'general_index_intro' => 'Bienvenue dans la routine d\'importation de Firefly III. Il existe différentes façons d\'importer des données dans Firefly III, affichées ici sous forme de boutons.', // import provider strings (index): - 'button_fake' => 'Fake an import', - 'button_file' => 'Import a file', - 'button_bunq' => 'Import from bunq', - 'button_spectre' => 'Import using Spectre', - 'button_plaid' => 'Import using Plaid', - 'button_yodlee' => 'Import using Yodlee', - 'button_quovo' => 'Import using Quovo', + 'button_fake' => 'Simuler une importation', + 'button_file' => 'Importer un fichier', + 'button_bunq' => 'Importer depuis bunq', + 'button_spectre' => 'Importer en utilisant Spectre', + 'button_plaid' => 'Importer en utilisant Plaid', + 'button_yodlee' => 'Importer en utilisant Yodlee', + 'button_quovo' => 'Importer en utilisant Quovo', // global config box (index) - 'global_config_title' => 'Global import configuration', - 'global_config_text' => 'In the future, this box will feature preferences that apply to ALL import providers above.', + 'global_config_title' => 'Configuration d\'importation globale', + 'global_config_text' => 'À l\'avenir, cette boîte contiendra les préférences qui s\'appliquent à TOUTES les sources d\'importation ci-dessus.', // prerequisites box (index) - 'need_prereq_title' => 'Import prerequisites', - 'need_prereq_intro' => 'Some import methods need your attention before they can be used. For example, they might require special API keys or application secrets. You can configure them here. The icon indicates if these prerequisites have been met.', - 'do_prereq_fake' => 'Prerequisites for the fake provider', - 'do_prereq_file' => 'Prerequisites for file imports', + 'need_prereq_title' => 'Prérequis d\'importation', + 'need_prereq_intro' => 'Certaines méthodes d\'importation nécessitent votre attention avant de pouvoir être utilisées. Par exemple, elles peuvent nécessiter des clés d\'API spéciales ou des clés secrètes. Vous pouvez les configurer ici. L\'icône indique si ces conditions préalables ont été remplies.', + 'do_prereq_fake' => 'Prérequis pour la simulation', + 'do_prereq_file' => 'Prérequis pour les importations de fichiers', 'do_prereq_bunq' => 'Prerequisites for imports from bunq', 'do_prereq_spectre' => 'Prerequisites for imports using Spectre', 'do_prereq_plaid' => 'Prerequisites for imports using Plaid', @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', @@ -204,61 +207,61 @@ return [ 'finished_with_errors' => 'There were some errors during the import. Please review them carefully.', 'unknown_import_result' => 'Unknown import result', 'result_no_transactions' => 'No transactions have been imported. Perhaps they were all duplicates is simply no transactions where present to be imported. Perhaps the log files can tell you what happened. If you import data regularly, this is normal.', - 'result_one_transaction' => 'Exactly one transaction has been imported. It is stored under tag :tag where you can inspect it further.', - 'result_many_transactions' => 'Firefly III has imported :count transactions. They are stored under tag :tag where you can inspect them further.', + 'result_one_transaction' => 'Une seule transaction a été importée. Elle est stockée sous le tag :tag où vous pouvez l\'afficher en détail.', + 'result_many_transactions' => 'Firefly III a importé :count transactions. Elles sont stockées sous le tag :tag où vous pouvez les afficher en détail.', // general errors and warnings: - 'bad_job_status' => 'To access this page, your import job cannot have status ":status".', + 'bad_job_status' => 'Vous ne pouvez pas accéder à cette page tant que l\'importation a le statut ":status".', // column roles for CSV import: 'column__ignore' => '(ignorer cette colonne)', 'column_account-iban' => 'Compte d’actif (IBAN)', - 'column_account-id' => 'Asset account ID (matching FF3)', + 'column_account-id' => 'Compte d\'actif (ID correspondant à FF3)', 'column_account-name' => 'Compte d’actif (nom)', 'column_amount' => 'Montant', - 'column_amount_foreign' => 'Amount (in foreign currency)', + 'column_amount_foreign' => 'Montant (en devise étrangère)', 'column_amount_debit' => 'Montant (colonne débit)', 'column_amount_credit' => 'Montant (colonne de crédit)', - 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'column_bill-id' => 'Bill ID (matching FF3)', + 'column_amount-comma-separated' => 'Montant (virgule comme séparateur décimal)', + 'column_bill-id' => 'Facture (ID correspondant à FF3)', 'column_bill-name' => 'Nom de la facture', - 'column_budget-id' => 'Budget ID (matching FF3)', + 'column_budget-id' => 'Budget (ID correspondant à FF3)', 'column_budget-name' => 'Nom du budget', - 'column_category-id' => 'Category ID (matching FF3)', + 'column_category-id' => 'Catégorie (ID correspondant à FF3)', 'column_category-name' => 'Nom de catégorie', - 'column_currency-code' => 'Currency code (ISO 4217)', - 'column_foreign-currency-code' => 'Foreign currency code (ISO 4217)', - 'column_currency-id' => 'Currency ID (matching FF3)', - 'column_currency-name' => 'Currency name (matching FF3)', - 'column_currency-symbol' => 'Currency symbol (matching FF3)', - 'column_date-interest' => 'Interest calculation date', - 'column_date-book' => 'Transaction booking date', - 'column_date-process' => 'Transaction process date', + 'column_currency-code' => 'Code de la devise (ISO 4217)', + 'column_foreign-currency-code' => 'Code de devise étrangère (ISO 4217)', + 'column_currency-id' => 'Devise (ID correspondant à FF3)', + 'column_currency-name' => 'Nom de la devise (correspondant à FF3)', + 'column_currency-symbol' => 'Symbole de la devise (correspondant à FF3)', + 'column_date-interest' => 'Date de calcul des intérêts', + 'column_date-book' => 'Date d\'enregistrement de la transaction', + 'column_date-process' => 'Date de traitement de la transaction', 'column_date-transaction' => 'Date', - 'column_date-due' => 'Transaction due date', - 'column_date-payment' => 'Transaction payment date', - 'column_date-invoice' => 'Transaction invoice date', + 'column_date-due' => 'Date d\'échéance de la transaction', + 'column_date-payment' => 'Date de paiement de la transaction', + 'column_date-invoice' => 'Date de facturation de la transaction', 'column_description' => 'Description', - 'column_opposing-iban' => 'Opposing account (IBAN)', - 'column_opposing-bic' => 'Opposing account (BIC)', - 'column_opposing-id' => 'Opposing account ID (matching FF3)', + 'column_opposing-iban' => 'Compte destinataire (IBAN)', + 'column_opposing-bic' => 'Compte destinataire (BIC)', + 'column_opposing-id' => 'Compte destinataire (ID correspondant à FF3)', 'column_external-id' => 'ID externe', - 'column_opposing-name' => 'Opposing account (name)', - 'column_rabo-debit-credit' => 'Rabobank specific debit/credit indicator', - 'column_ing-debit-credit' => 'ING specific debit/credit indicator', - 'column_sepa-ct-id' => 'SEPA end-to-end Identifier', - 'column_sepa-ct-op' => 'SEPA Opposing Account Identifier', - 'column_sepa-db' => 'SEPA Mandate Identifier', - 'column_sepa-cc' => 'SEPA Clearing Code', - 'column_sepa-ci' => 'SEPA Creditor Identifier', - 'column_sepa-ep' => 'SEPA External Purpose', - 'column_sepa-country' => 'SEPA Country Code', + 'column_opposing-name' => 'Compte destinataire (nom)', + 'column_rabo-debit-credit' => 'Indicateur de débit/crédit spécifique à Rabobank', + 'column_ing-debit-credit' => 'Indicateur de débit/crédit spécifique à ING', + 'column_sepa-ct-id' => 'Référence de bout en bout SEPA', + 'column_sepa-ct-op' => 'Référence SEPA du compte destinataire', + 'column_sepa-db' => 'Référence Unique de Mandat SEPA', + 'column_sepa-cc' => 'Code de rapprochement SEPA', + 'column_sepa-ci' => 'Identifiant Créancier SEPA', + 'column_sepa-ep' => 'Objectif externe SEPA', + 'column_sepa-country' => 'Code de pays SEPA', 'column_tags-comma' => 'Tags (séparés par des virgules)', 'column_tags-space' => 'Tags (séparé par un espace)', - 'column_account-number' => 'Asset account (account number)', - 'column_opposing-number' => 'Opposing account (account number)', + 'column_account-number' => 'Compte d’actif (numéro de compte)', + 'column_opposing-number' => 'Compte destinataire (numéro de compte)', 'column_note' => 'Note(s)', - 'column_internal-reference' => 'Internal reference', + 'column_internal-reference' => 'Référence interne', ]; diff --git a/resources/lang/fr_FR/intro.php b/resources/lang/fr_FR/intro.php index 103c316b23..cfed1ec1ca 100644 --- a/resources/lang/fr_FR/intro.php +++ b/resources/lang/fr_FR/intro.php @@ -93,7 +93,7 @@ return [ 'piggy-banks_show_piggyEvents' => 'Des ajouts ou suppressions sont également répertoriées ici.', // bill index - 'bills_index_rules' => 'Here you see which rules will check if this bill is hit', + 'bills_index_rules' => 'Ici, vous voyez quelles règles vont s\'appliquer si cette facture est payée', 'bills_index_paid_in_period' => 'Ce champ indique quand la facture a été payée pour la dernière fois.', 'bills_index_expected_in_period' => 'Ce champ indique pour chaque facture si et quand la facture suivante est attendue.', @@ -103,12 +103,12 @@ return [ 'bills_show_billChart' => 'Ce tableau montre les transactions liées à cette facture.', // create bill - 'bills_create_intro' => 'Use bills to track the amount of money you\'re due every period. Think about expenses like rent, insurance or mortgage payments.', + 'bills_create_intro' => 'Utilisez des factures pour suivre les sommes que vous avez à payer à chaque période. Pensez aux dépenses comme le loyer, l\'assurance ou les remboursements d\'emprunts.', 'bills_create_name' => 'Utilisez un nom équivoque tel que "Loyer" ou "Assurance maladie".', //'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' => 'Sélectionnez un montant minimum et maximum pour cette facture.', 'bills_create_repeat_freq_holder' => 'La plupart des factures sont mensuelles, mais vous pouvez définir une autre fréquence ici.', - 'bills_create_skip_holder' => 'If a bill repeats every 2 weeks, the "skip"-field should be set to "1" to skip every other week.', + 'bills_create_skip_holder' => 'Si une facture se répète toutes les 2 semaines, le champ "sauter" doit être réglé sur "1" pour sauter une semaine sur deux.', // rules index 'rules_index_intro' => 'Firefly III vous permet de gérer les règles, qui seront automagiquement appliquées à toute transaction que vous créez ou modifiez.', diff --git a/resources/lang/fr_FR/list.php b/resources/lang/fr_FR/list.php index ccafd1e385..c2d0261328 100644 --- a/resources/lang/fr_FR/list.php +++ b/resources/lang/fr_FR/list.php @@ -34,7 +34,7 @@ return [ 'name' => 'Nom', 'role' => 'Rôle', 'currentBalance' => 'Solde courant', - 'linked_to_rules' => 'Relevant rules', + 'linked_to_rules' => 'Règles applicables', 'active' => 'Actif ?', 'lastActivity' => 'Activité récente', 'balanceDiff' => 'Différence d\'équilibre', @@ -112,15 +112,20 @@ return [ 'sepa-cc' => 'Code de compensation SEPA', 'sepa-ep' => 'Objectif externe SEPA', 'sepa-ci' => 'Identifiant SEPA Creditor', - 'external_id' => 'External ID', + 'external_id' => 'ID externe', 'account_at_bunq' => 'Compte avec bunq', - 'file_name' => 'File name', - 'file_size' => 'File size', - 'file_type' => 'File type', - 'attached_to' => 'Attached to', - 'file_exists' => 'File exists', - 'spectre_bank' => 'Bank', - 'spectre_last_use' => 'Last login', - 'spectre_status' => 'Status', - 'bunq_payment_id' => 'bunq payment ID', + 'file_name' => 'Nom du fichier', + 'file_size' => 'Taille du fichier', + 'file_type' => 'Type de fichier', + 'attached_to' => 'Attaché à', + 'file_exists' => 'Le fichier existe', + 'spectre_bank' => 'Banque', + 'spectre_last_use' => 'Dernière connexion', + 'spectre_status' => 'Statut', + 'bunq_payment_id' => 'ID de paiement bunq', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transaction(s)', + 'field' => 'Field', + 'value' => 'Value', ]; diff --git a/resources/lang/fr_FR/validation.php b/resources/lang/fr_FR/validation.php index 6ce0d06474..e5277aa087 100644 --- a/resources/lang/fr_FR/validation.php +++ b/resources/lang/fr_FR/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Il ne s\'agit pas d\'un IBAN valide.', - 'source_equals_destination' => 'Le compte source est égal au compte de destination', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Il semble que ce numéro de compte est déjà utilisé.', 'unique_iban_for_user' => 'Il semble que cet IBAN soit déjà utilisé.', 'deleted_user' => 'Compte tenu des contraintes de sécurité, vous ne pouvez pas vous inscrire en utilisant cette adresse e-mail.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Envoi du fichier ":name" avec succès.', 'must_exist' => 'L\'ID dans le champ :attribute n\'existe pas dans la base de données.', 'all_accounts_equal' => 'Tous les comptes dans ce champ doivent être égaux.', - 'invalid_selection' => 'Votre sélection est invalide', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'Cette valeur n\'est pas valide pour ce champ.', 'at_least_one_transaction' => 'Besoin d\'au moins une transaction.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'Le contenu de ce champ n\'est pas valide sans informations sur la devise.', 'equal_description' => 'La description de la transaction ne doit pas être égale à la description globale.', 'file_invalid_mime' => 'Le fichier ":name" est du type ":mime" ce qui n\'est pas accepté pour un nouvel envoi.', 'file_too_large' => 'Le fichier ":name" est trop grand.', - 'belongs_to_user' => 'La valeur de :attribute est inconnue', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => 'Le champ :attribute doit être accepté.', 'bic' => 'Ce n’est pas un code BIC valide.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute doit être supérieur à zéro.', 'active_url' => 'Le champ :attribute n\'est pas une URL valide.', 'after' => 'Le champ :attribute doit être une date postérieure à :date.', @@ -53,8 +59,8 @@ return [ 'array' => 'Le champ :attribute doit être un tableau.', 'unique_for_user' => 'Il existe déjà une entrée avec ceci :attribute.', 'before' => 'Le champ :attribute doit être une date antérieure à :date.', - 'unique_object_for_user' => 'Ce nom est déjà utilisé', - 'unique_account_for_user' => 'Ce nom de compte est déjà utilisé', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => 'La valeur de :attribute doit être comprise entre :min et :max.', 'between.file' => 'Le fichier :attribute doit avoir une taille entre :min et :max kilo-octets.', 'between.string' => 'Le texte :attribute doit avoir entre :min et :max caractères.', @@ -85,6 +91,9 @@ return [ 'min.array' => 'Le tableau :attribute doit avoir au moins :min éléments.', 'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.', 'numeric' => 'Le champ :attribute doit contenir un nombre.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'Le format du champ :attribute est invalide.', 'required' => 'Le champ :attribute est obligatoire.', 'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.', @@ -109,9 +118,12 @@ return [ 'file' => 'Le :attribute doit être un fichier.', 'in_array' => 'Le champ :attribute n\'existe pas dans :other.', 'present' => 'Le champs :attribute doit être rempli.', - 'amount_zero' => 'Le montant total ne peut pas être zéro', - 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'amount_zero' => 'The total amount cannot be zero.', + 'unique_piggy_bank_for_user' => 'Le nom de la tirelire doit être unique.', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'adresse email', 'description' => 'description', diff --git a/resources/lang/id_ID/config.php b/resources/lang/id_ID/config.php index 64659774cd..42f77dcca9 100644 --- a/resources/lang/id_ID/config.php +++ b/resources/lang/id_ID/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'id', - 'locale' => 'id, Bahasa Indonesia, id_ID, id_ID.utf8, id_ID.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Minggu %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'id', + 'locale' => 'id, Bahasa Indonesia, id_ID, id_ID.utf8, id_ID.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Minggu %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'MMMM Do, YYYY', + 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Monday', + 'dow_2' => 'Tuesday', + 'dow_3' => 'Wednesday', + 'dow_4' => 'Thursday', + 'dow_5' => 'Friday', + 'dow_6' => 'Saturday', + 'dow_7' => 'Sunday', ]; diff --git a/resources/lang/id_ID/demo.php b/resources/lang/id_ID/demo.php index a62c929dc0..0489b67c41 100644 --- a/resources/lang/id_ID/demo.php +++ b/resources/lang/id_ID/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Biaya ini, deposito dan transfer tidak terlalu imajinatif. Mereka telah dihasilkan secara otomatis.', 'piggy-banks-index' => 'Seperti yang bisa Anda lihat, ada tiga celengan. Gunakan tombol plus dan minus untuk mempengaruhi jumlah uang di setiap celengan. Klik nama celengan untuk melihat administrasi masing-masing celengan.', 'import-index' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/id_ID/firefly.php b/resources/lang/id_ID/firefly.php index 348ed93b3a..d5a358e8a8 100644 --- a/resources/lang/id_ID/firefly.php +++ b/resources/lang/id_ID/firefly.php @@ -465,6 +465,7 @@ return [ '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' => 'Setel ulang kode verifikasi', 'pref_two_factor_auth_disable_2fa' => 'Disable 2FA', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Simpan Pengaturan', 'saved_preferences' => 'Preferensi disimpan!', 'preferences_general' => 'Umum', @@ -820,7 +821,7 @@ return [ 'language' => 'Language', 'new_savings_account' => ':bank_name savings account', 'cash_wallet' => 'Cash wallet', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', // home page: 'yourAccounts' => 'Akun anda', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Saldo akhir periode', 'splitByAccount' => 'Dibagi oleh akun', 'coveredWithTags' => 'Ditutupi dengan tag', - 'leftUnbalanced' => 'Meninggalkan tidak seimbang', 'leftInBudget' => 'Yang tersisa di anggaran', 'sumOfSums' => 'Jumlah dari jumlah', 'noCategory' => '(Tidak ada kategori)', @@ -1062,7 +1062,7 @@ return [ 'instance_configuration' => 'Konfigurasi', 'firefly_instance_configuration' => 'Pilihan konfigurasi untuk Firefly III', 'setting_single_user_mode' => 'Mode pengguna tunggal', - 'setting_single_user_mode_explain' => 'Secara default, Firefly III hanya menerima satu (1) registrasi: anda. Ini adalah tindakan pengamanan, mencegah orang lain menggunakan contoh Anda kecuali jika Anda mengizinkannya melakukannya. Pendaftaran di masa depan diblokir Bila Anda tidak mencentang kotak ini, orang lain dapat menggunakan contoh Anda dengan baik, dengan asumsi mereka dapat mencapainya (bila terhubung ke internet).', + '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 well, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Konfigurasi toko', 'single_user_administration' => 'Administrasi pengguna untuk :email', 'edit_user' => 'Edit pengguna :email', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => '(sebagian) dikembalikan oleh', 'is (partially) paid for by_inward' => 'adalah (sebagian) dibayar oleh', 'is (partially) reimbursed by_inward' => '(sebagian) diganti oleh', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'berhubungan dengan', '(partially) refunds_outward' => '(sebagian) pengembalian uang', '(partially) pays for_outward' => '(sebagian) membayar', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Tidak dapat mengonversi transaksi split', // Import page (general strings only) - 'import_index_title' => 'Impor data ke Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Impor data', + 'import_transactions' => 'Import transactions', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Fungsi ini tidak tersedia saat Anda menggunakan Firefly III di dalam lingkungan Sandstorm.io.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Anda belum memiliki tagihan. Anda bisa membuat tagihan untuk mencatat pengeluaran rutin, seperti sewa atau asuransi Anda.', 'no_bills_imperative_default' => 'Apakah Anda memiliki tagihan reguler seperti itu? Buat tagihan dan lacak pembayaran Anda:', 'no_bills_create_default' => 'Buat tagihan', + + // recurring transactions + 'recurrences' => 'Recurring transactions', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Create a recurring transaction', + 'make_new_recurring' => 'Create a recurring transaction', + 'recurring_daily' => 'Every day', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Related transactions', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notes', + 'recurring_meta_field_bill_id' => 'Bill', + 'recurring_meta_field_piggy_bank_id' => 'Piggy bank', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/id_ID/form.php b/resources/lang/id_ID/form.php index 6dd307210f..081ce56d1b 100644 --- a/resources/lang/id_ID/form.php +++ b/resources/lang/id_ID/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Nama Bank', - 'bank_balance' => 'Keseimbangan', - 'savings_balance' => 'Saldo tabungan', - 'credit_card_limit' => 'Batas kartu kredit', - 'automatch' => 'Cocokkan secara otomatis', - 'skip' => 'Melewatkan', - 'name' => 'Nama', - 'active' => 'Aktif', - 'amount_min' => 'Jumlah minimal', - 'amount_max' => 'Jumlah maksimum', - 'match' => 'Cocok di', - 'strict' => 'Strict mode', - 'repeat_freq' => 'Berulang', - 'journal_currency_id' => 'Mata uang', - 'currency_id' => 'Mata uang', - 'transaction_currency_id' => 'Currency', - 'external_ip' => 'Your server\'s external IP', - 'attachments' => 'Lampiran', - 'journal_amount' => 'Jumlah', - 'journal_source_account_name' => 'Akun pendapatan (sumber)', - 'journal_source_account_id' => 'Akun aset (sumber)', - 'BIC' => 'BIC', - 'verify_password' => 'Verifikasi keamanan kata sandi', - 'source_account' => 'Akun sumber', - 'destination_account' => 'Akun tujuan', - 'journal_destination_account_id' => 'Akun aset (tujuan)', - 'asset_destination_account' => 'Akun aset (tujuan)', - 'asset_source_account' => 'Akun aset (sumber)', - 'journal_description' => 'Deskripsi', - 'note' => 'Catatan', - 'split_journal' => 'Pisahkan transaksi ini', - 'split_journal_explanation' => 'Split transaksi ini di banyak bagian', - 'currency' => 'Mata uang', - 'account_id' => 'Akun aset', - 'budget_id' => 'Anggaran', - 'openingBalance' => 'Saldo awal', - 'tagMode' => 'Mode Tag', - 'tag_position' => 'Lokasi tag', - 'virtualBalance' => 'Saldo virtual', - 'targetamount' => 'Jumlah target', - 'accountRole' => 'Peran akun', - 'openingBalanceDate' => 'Membuka tanggal saldo', - 'ccType' => 'Rencana pembayaran kartu kredit', - 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', - 'piggy_bank_id' => 'Celengan', - 'returnHere' => 'Kembali ke sini', - 'returnHereExplanation' => 'Setelah menyimpan, kembali ke sini untuk membuat yang lain.', - 'returnHereUpdateExplanation' => 'Setelah update, kembali ke sini.', - 'description' => 'Deskripsi', - 'expense_account' => 'Rekening pengeluaran', - 'revenue_account' => 'Akun pendapatan', - 'decimal_places' => 'Tempat desimal', - 'exchange_rate_instruction' => 'Mata uang asing', - 'source_amount' => 'Jumlah (sumber)', - 'destination_amount' => 'Jumlah (tujuan)', - 'native_amount' => 'Jumlah asli', - 'new_email_address' => 'Alamat email baru', - 'verification' => 'Verifikasi', - 'api_key' => 'Kunci API', - 'remember_me' => 'Remember me', + 'bank_name' => 'Nama Bank', + 'bank_balance' => 'Keseimbangan', + 'savings_balance' => 'Saldo tabungan', + 'credit_card_limit' => 'Batas kartu kredit', + 'automatch' => 'Cocokkan secara otomatis', + 'skip' => 'Melewatkan', + 'name' => 'Nama', + 'active' => 'Aktif', + 'amount_min' => 'Jumlah minimal', + 'amount_max' => 'Jumlah maksimum', + 'match' => 'Cocok di', + 'strict' => 'Strict mode', + 'repeat_freq' => 'Berulang', + 'journal_currency_id' => 'Mata uang', + 'currency_id' => 'Mata uang', + 'transaction_currency_id' => 'Currency', + 'external_ip' => 'Your server\'s external IP', + 'attachments' => 'Lampiran', + 'journal_amount' => 'Jumlah', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Verifikasi keamanan kata sandi', + 'source_account' => 'Akun sumber', + 'destination_account' => 'Akun tujuan', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Akun aset (tujuan)', + 'asset_source_account' => 'Akun aset (sumber)', + 'journal_description' => 'Deskripsi', + 'note' => 'Catatan', + 'split_journal' => 'Pisahkan transaksi ini', + 'split_journal_explanation' => 'Split transaksi ini di banyak bagian', + 'currency' => 'Mata uang', + 'account_id' => 'Akun aset', + 'budget_id' => 'Anggaran', + 'openingBalance' => 'Saldo awal', + 'tagMode' => 'Mode Tag', + 'tag_position' => 'Lokasi tag', + 'virtualBalance' => 'Saldo virtual', + 'targetamount' => 'Jumlah target', + 'accountRole' => 'Peran akun', + 'openingBalanceDate' => 'Membuka tanggal saldo', + 'ccType' => 'Rencana pembayaran kartu kredit', + 'ccMonthlyPaymentDate' => 'Credit card monthly payment date', + 'piggy_bank_id' => 'Celengan', + 'returnHere' => 'Kembali ke sini', + 'returnHereExplanation' => 'Setelah menyimpan, kembali ke sini untuk membuat yang lain.', + 'returnHereUpdateExplanation' => 'Setelah update, kembali ke sini.', + 'description' => 'Deskripsi', + 'expense_account' => 'Rekening pengeluaran', + 'revenue_account' => 'Akun pendapatan', + 'decimal_places' => 'Tempat desimal', + 'exchange_rate_instruction' => 'Mata uang asing', + 'source_amount' => 'Jumlah (sumber)', + 'destination_amount' => 'Jumlah (tujuan)', + 'native_amount' => 'Jumlah asli', + 'new_email_address' => 'Alamat email baru', + 'verification' => 'Verifikasi', + 'api_key' => 'Kunci API', + 'remember_me' => 'Remember me', 'source_account_asset' => 'Akun sumber (akun aset)', 'destination_account_expense' => 'Akun tujuan (akun pengeluaran)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Convert deposit', 'convert_Transfer' => 'Mengkonversi transfer', - 'amount' => 'Jumlah', - 'foreign_amount' => 'Foreign amount', - 'existing_attachments' => 'Existing attachments', - 'date' => 'Tanggal', - 'interest_date' => 'Tanggal bunga', - 'book_date' => 'Tanggal buku', - 'process_date' => 'Tanggal pemrosesan', - 'category' => 'Kategori', - 'tags' => 'Tag', - 'deletePermanently' => 'Hapus secara permanen', - 'cancel' => 'Membatalkan', - 'targetdate' => 'Tanggal target', - 'startdate' => 'Mulai tanggal', - 'tag' => 'Menandai', - 'under' => 'Dibawah', - 'symbol' => 'Simbol', - 'code' => 'Kode', - 'iban' => 'IBAN', - 'accountNumber' => 'Nomor akun', - 'creditCardNumber' => 'Nomor kartu kredit', - 'has_headers' => 'Judul', - 'date_format' => 'Format tanggal', - 'specifix' => 'Perbaikan spesifik bank atau berkas', - 'attachments[]' => 'Lampiran', - 'store_new_withdrawal' => 'Simpan penarikan baru', - 'store_new_deposit' => 'Simpan deposit baru', - 'store_new_transfer' => 'Simpan transfer baru', - 'add_new_withdrawal' => 'Tambahkan penarikan baru', - 'add_new_deposit' => 'Tambahkan deposit baru', - 'add_new_transfer' => 'Tambahkan transfer baru', - 'title' => 'Judul', - 'notes' => 'Catatan', - 'filename' => 'Nama file', - 'mime' => 'Tipe mime', - 'size' => 'Ukuran', - 'trigger' => 'Pelatuk', - 'stop_processing' => 'Berhenti memproses', - 'start_date' => 'Mulai dari jangkauan', - 'end_date' => 'Akhir rentang', - 'export_start_range' => 'Mulai dari rentang ekspor', - 'export_end_range' => 'Akhir rentang ekspor', - 'export_format' => 'Format file', - 'include_attachments' => 'Sertakan lampiran yang diunggah', - 'include_old_uploads' => 'Sertakan data yang diimpor', - 'accounts' => 'Mengekspor transaksi dari akun ini', - 'delete_account' => 'Delete account ":name"', - 'delete_bill' => 'Hapus tagihan ":name"', - 'delete_budget' => 'Hapus anggaran ":name"', - 'delete_category' => 'Hapus kategori ":name"', - 'delete_currency' => 'Hapus mata uang ":name"', - 'delete_journal' => 'Hapus transaksi dengan deskripsi ":description"', - 'delete_attachment' => 'Hapus lampiran ":name"', - 'delete_rule' => 'Hapus aturan ":title"', - 'delete_rule_group' => 'Hapus grup aturan ":title"', - 'delete_link_type' => 'Hapus jenis tautan ":name"', - 'delete_user' => 'Hapus pengguna ":email"', - 'user_areYouSure' => 'Jika Anda menghapus pengguna ":email", semuanya akan hilang. Tidak ada undo, undelete atau apapun. Jika Anda menghapus diri Anda sendiri, Anda akan kehilangan akses ke Firefly III ini.', - 'attachment_areYouSure' => 'Yakin ingin menghapus lampiran yang bernama ":name"?', - 'account_areYouSure' => 'Yakin ingin menghapus akun dengan nama ":name"?', - 'bill_areYouSure' => 'Yakin ingin menghapus tagihan yang bernama ":name"?', - 'rule_areYouSure' => 'Yakin ingin menghapus aturan yang berjudul ":title"?', - 'ruleGroup_areYouSure' => 'Yakin ingin menghapus grup aturan yang berjudul ":title"?', - 'budget_areYouSure' => 'Yakin ingin menghapus anggaran dengan nama ":name"?', - 'category_areYouSure' => 'Yakin ingin menghapus kategori yang bernama ":name"?', - 'currency_areYouSure' => 'Yakin ingin menghapus mata uang dengan nama ":name"?', - 'piggyBank_areYouSure' => 'Yakin ingin menghapus piggy bank yang bernama ":name"?', - 'journal_areYouSure' => 'Yakin ingin menghapus transaksi yang dijelaskan ":description"?', - 'mass_journal_are_you_sure' => 'Yakin ingin menghapus transaksi ini?', - 'tag_areYouSure' => 'Yakin ingin menghapus tag ":tag"?', - 'journal_link_areYouSure' => 'Yakin ingin menghapus tautan antara :source and :destination?', - 'linkType_areYouSure' => 'Yakin ingin menghapus jenis tautan ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', - 'mass_make_selection' => 'Anda masih dapat mencegah agar item dihapus dengan menghapus kotak centang.', - 'delete_all_permanently' => 'Hapus yang dipilih secara permanen', - 'update_all_journals' => 'Perbarui transaksi ini', - 'also_delete_transactions' => 'Satu-satunya transaksi yang terhubung ke akun ini akan dihapus juga. | Semua :count transaksi yang terhubung ke akun ini akan dihapus juga.', - 'also_delete_connections' => 'Satu-satunya transaksi yang terkait dengan jenis link ini akan kehilangan koneksi ini. Semua :count transaksi yang terkait dengan jenis link ini akan kehilangan koneksi mereka.', - 'also_delete_rules' => 'Aturan satu-satunya yang terhubung ke grup aturan ini akan dihapus juga. Aturan All :count yang terhubung ke grup aturan ini akan dihapus juga.', - 'also_delete_piggyBanks' => 'Satu-satunya piggy bank yang terhubung ke akun ini akan dihapus juga. Semua :count piggy bank yang terhubung ke akun ini akan dihapus juga.', - 'bill_keep_transactions' => 'Satu-satunya transaksi yang terhubung dengan tagihan ini tidak akan dihapus. Semua :count transaksi yang terhubung ke tagihan ini akan terhindar dari penghapusan.', - 'budget_keep_transactions' => 'Satu-satunya transaksi yang terhubung dengan anggaran ini tidak akan dihapus. Semua :count transaksi yang terhubung dengan anggaran ini akan terhindar dari penghapusan.', - 'category_keep_transactions' => 'Satu-satunya transaksi yang terhubung ke kategori ini tidak akan dihapus. Semua :count transaksi yang terhubung ke kategori ini akan terhindar dari penghapusan.', - 'tag_keep_transactions' => 'Satu-satunya transaksi yang terhubung ke tag ini tidak akan dihapus. Semua :count transaksi yang terhubung ke tag ini akan terhindar dari penghapusan.', - 'check_for_updates' => 'Check for updates', + 'amount' => 'Jumlah', + 'foreign_amount' => 'Foreign amount', + 'existing_attachments' => 'Existing attachments', + 'date' => 'Tanggal', + 'interest_date' => 'Tanggal bunga', + 'book_date' => 'Tanggal buku', + 'process_date' => 'Tanggal pemrosesan', + 'category' => 'Kategori', + 'tags' => 'Tag', + 'deletePermanently' => 'Hapus secara permanen', + 'cancel' => 'Membatalkan', + 'targetdate' => 'Tanggal target', + 'startdate' => 'Mulai tanggal', + 'tag' => 'Menandai', + 'under' => 'Dibawah', + 'symbol' => 'Simbol', + 'code' => 'Kode', + 'iban' => 'IBAN', + 'accountNumber' => 'Nomor akun', + 'creditCardNumber' => 'Nomor kartu kredit', + 'has_headers' => 'Judul', + 'date_format' => 'Format tanggal', + 'specifix' => 'Perbaikan spesifik bank atau berkas', + 'attachments[]' => 'Lampiran', + 'store_new_withdrawal' => 'Simpan penarikan baru', + 'store_new_deposit' => 'Simpan deposit baru', + 'store_new_transfer' => 'Simpan transfer baru', + 'add_new_withdrawal' => 'Tambahkan penarikan baru', + 'add_new_deposit' => 'Tambahkan deposit baru', + 'add_new_transfer' => 'Tambahkan transfer baru', + 'title' => 'Judul', + 'notes' => 'Catatan', + 'filename' => 'Nama file', + 'mime' => 'Tipe mime', + 'size' => 'Ukuran', + 'trigger' => 'Pelatuk', + 'stop_processing' => 'Berhenti memproses', + 'start_date' => 'Mulai dari jangkauan', + 'end_date' => 'Akhir rentang', + 'export_start_range' => 'Mulai dari rentang ekspor', + 'export_end_range' => 'Akhir rentang ekspor', + 'export_format' => 'Format file', + 'include_attachments' => 'Sertakan lampiran yang diunggah', + 'include_old_uploads' => 'Sertakan data yang diimpor', + 'accounts' => 'Mengekspor transaksi dari akun ini', + 'delete_account' => 'Delete account ":name"', + 'delete_bill' => 'Hapus tagihan ":name"', + 'delete_budget' => 'Hapus anggaran ":name"', + 'delete_category' => 'Hapus kategori ":name"', + 'delete_currency' => 'Hapus mata uang ":name"', + 'delete_journal' => 'Hapus transaksi dengan deskripsi ":description"', + 'delete_attachment' => 'Hapus lampiran ":name"', + 'delete_rule' => 'Hapus aturan ":title"', + 'delete_rule_group' => 'Hapus grup aturan ":title"', + 'delete_link_type' => 'Hapus jenis tautan ":name"', + 'delete_user' => 'Hapus pengguna ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Jika Anda menghapus pengguna ":email", semuanya akan hilang. Tidak ada undo, undelete atau apapun. Jika Anda menghapus diri Anda sendiri, Anda akan kehilangan akses ke Firefly III ini.', + 'attachment_areYouSure' => 'Yakin ingin menghapus lampiran yang bernama ":name"?', + 'account_areYouSure' => 'Yakin ingin menghapus akun dengan nama ":name"?', + 'bill_areYouSure' => 'Yakin ingin menghapus tagihan yang bernama ":name"?', + 'rule_areYouSure' => 'Yakin ingin menghapus aturan yang berjudul ":title"?', + 'ruleGroup_areYouSure' => 'Yakin ingin menghapus grup aturan yang berjudul ":title"?', + 'budget_areYouSure' => 'Yakin ingin menghapus anggaran dengan nama ":name"?', + 'category_areYouSure' => 'Yakin ingin menghapus kategori yang bernama ":name"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => 'Yakin ingin menghapus mata uang dengan nama ":name"?', + 'piggyBank_areYouSure' => 'Yakin ingin menghapus piggy bank yang bernama ":name"?', + 'journal_areYouSure' => 'Yakin ingin menghapus transaksi yang dijelaskan ":description"?', + 'mass_journal_are_you_sure' => 'Yakin ingin menghapus transaksi ini?', + 'tag_areYouSure' => 'Yakin ingin menghapus tag ":tag"?', + 'journal_link_areYouSure' => 'Yakin ingin menghapus tautan antara :source and :destination?', + 'linkType_areYouSure' => 'Yakin ingin menghapus jenis tautan ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', + 'mass_make_selection' => 'Anda masih dapat mencegah agar item dihapus dengan menghapus kotak centang.', + 'delete_all_permanently' => 'Hapus yang dipilih secara permanen', + 'update_all_journals' => 'Perbarui transaksi ini', + 'also_delete_transactions' => 'Satu-satunya transaksi yang terhubung ke akun ini akan dihapus juga. | Semua :count transaksi yang terhubung ke akun ini akan dihapus juga.', + 'also_delete_connections' => 'Satu-satunya transaksi yang terkait dengan jenis link ini akan kehilangan koneksi ini. Semua :count transaksi yang terkait dengan jenis link ini akan kehilangan koneksi mereka.', + 'also_delete_rules' => 'Aturan satu-satunya yang terhubung ke grup aturan ini akan dihapus juga. Aturan All :count yang terhubung ke grup aturan ini akan dihapus juga.', + 'also_delete_piggyBanks' => 'Satu-satunya piggy bank yang terhubung ke akun ini akan dihapus juga. Semua :count piggy bank yang terhubung ke akun ini akan dihapus juga.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Check for updates', 'email' => 'Alamat email', 'password' => 'Kata sandi', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Kode negara', 'provider_code' => 'Bank atau penyedia data', - 'due_date' => 'Batas tanggal terakhir', - 'payment_date' => 'Tanggal pembayaran', - 'invoice_date' => 'Tanggal faktur', - 'internal_reference' => 'Referensi internal', - 'inward' => 'Deskripsi dalam', - 'outward' => 'Deskripsi luar', - 'rule_group_id' => 'Kelompok aturan', + 'due_date' => 'Batas tanggal terakhir', + 'payment_date' => 'Tanggal pembayaran', + 'invoice_date' => 'Tanggal faktur', + 'internal_reference' => 'Referensi internal', + 'inward' => 'Deskripsi dalam', + 'outward' => 'Deskripsi luar', + 'rule_group_id' => 'Kelompok aturan', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/id_ID/import.php b/resources/lang/id_ID/import.php index 93809b1375..e726d3e7a7 100644 --- a/resources/lang/id_ID/import.php +++ b/resources/lang/id_ID/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/id_ID/list.php b/resources/lang/id_ID/list.php index d64698938c..98106680de 100644 --- a/resources/lang/id_ID/list.php +++ b/resources/lang/id_ID/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Last login', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq payment ID', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transaction(s)', + 'field' => 'Field', + 'value' => 'Value', ]; diff --git a/resources/lang/id_ID/validation.php b/resources/lang/id_ID/validation.php index 00be6153a5..3175395fc5 100644 --- a/resources/lang/id_ID/validation.php +++ b/resources/lang/id_ID/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Ini bukan IBAN yang valid.', - 'source_equals_destination' => 'The source account equals the destination account', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Sepertinya nomor rekening ini sudah digunakan.', 'unique_iban_for_user' => 'It looks like this IBAN is already in use.', 'deleted_user' => 'Kerena kendala keamanan, anda tidak bisa mendaftar menggunkan alamat email ini.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'File yang diupload dengan sukses ":name.', 'must_exist' => 'The ID in field :attribute does not exist in the database.', 'all_accounts_equal' => 'All accounts in this field must be equal.', - 'invalid_selection' => 'Your selection is invalid', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'This value is invalid for this field.', 'at_least_one_transaction' => 'Need at least one transaction.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'The content of this field is invalid without currency information.', 'equal_description' => 'Transaction description should not equal global description.', 'file_invalid_mime' => 'File ":name" adalah tipe ":mime" yang tidak diterima sebagai upload baru.', 'file_too_large' => 'File "; name" terlalu besar.', - 'belongs_to_user' => 'Nilai dari :attribute tidak diketahui', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => ':attribute harus diterima.', 'bic' => 'Ini bukan BIC yang valid.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute harus lebih besar dari nol.', 'active_url' => ':attribute bukan URL yang valid.', 'after' => ':attribute harus tanggal setelah :date.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute harus berupa array.', 'unique_for_user' => 'Sudah ada entri dengan :attribute ini.', 'before' => ':attribute harus tanggal sebelum :date.', - 'unique_object_for_user' => 'Nama ini sudah digunakan', - 'unique_account_for_user' => 'Nama akun ini sudah digunakan', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => ':attribute harus antara :min dan :max.', 'between.file' => ':attribute harus antara :min dan :max kilobyte.', 'between.string' => ':attribute harus antara :min dan :max karakter.', @@ -85,6 +91,9 @@ return [ 'min.array' => ':attribute harus minimal item :min.', 'not_in' => ':attribute yang dipilih tidak valid.', 'numeric' => ':attribute harus angka.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'Format :attribute tidak valid.', 'required' => 'Bidang :attribute diperlukan.', 'required_if' => 'Bidang :attribute diperlukan ketika :other adalah :value.', @@ -109,9 +118,12 @@ return [ 'file' => ':attribute harus berupa file.', 'in_array' => 'Bidang :attribute tidak ada in :other.', 'present' => 'Bidang :attribute harus ada.', - 'amount_zero' => 'Jumlah total tidak boleh nol', + 'amount_zero' => 'The total amount cannot be zero.', 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'email address', 'description' => 'description', diff --git a/resources/lang/it_IT/breadcrumbs.php b/resources/lang/it_IT/breadcrumbs.php index f631ee70e8..aa8ff8bb13 100644 --- a/resources/lang/it_IT/breadcrumbs.php +++ b/resources/lang/it_IT/breadcrumbs.php @@ -23,7 +23,7 @@ declare(strict_types=1); return [ - 'home' => 'Home', + 'home' => 'Pagina principale', 'edit_currency' => 'Modifica valuta ":name"', 'delete_currency' => 'Elimina valuta ":name"', 'newPiggyBank' => 'Crea un nuovo salvadanaio', @@ -39,7 +39,7 @@ return [ 'reports' => 'Resoconti', 'search_result' => 'Risultati di ricerca per ":query"', 'withdrawal_list' => 'Spese', - 'deposit_list' => 'Reddito, entrate e depositi', + 'deposit_list' => 'Redditi, entrate e depositi', 'transfer_list' => 'Trasferimenti', 'transfers_list' => 'Trasferimenti', 'reconciliation_list' => 'Riconciliazioni', diff --git a/resources/lang/it_IT/config.php b/resources/lang/it_IT/config.php index 667e347c13..8ba46bb310 100644 --- a/resources/lang/it_IT/config.php +++ b/resources/lang/it_IT/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'it', - 'locale' => 'it, Italiano, it_IT, it_IT.utf8, it_IT.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Settimana %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM AAAA', - 'month_and_day_js' => 'Do MMMM YYYY', - 'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss', - 'specific_day_js' => 'G MMMM AAAA', - 'week_in_year_js' => '[Week] s, AAAA', - 'year_js' => 'AAAA', - 'half_year_js' => 'T AAAA', + 'html_language' => 'it', + 'locale' => 'it, Italiano, it_IT, it_IT.utf8, it_IT.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %B %e %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Settimana %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM AAAA', + 'month_and_day_js' => 'Do MMMM YYYY', + 'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss', + 'specific_day_js' => 'G MMMM AAAA', + 'week_in_year_js' => '[Week] s, AAAA', + 'year_js' => 'AAAA', + 'half_year_js' => 'T AAAA', + 'dow_1' => 'Lunedì', + 'dow_2' => 'Martedì', + 'dow_3' => 'Mercoledì', + 'dow_4' => 'Giovedì', + 'dow_5' => 'Venerdì', + 'dow_6' => 'Sabato', + 'dow_7' => 'Domenica', ]; diff --git a/resources/lang/it_IT/demo.php b/resources/lang/it_IT/demo.php index 38c210d8fb..3a3f7fda89 100644 --- a/resources/lang/it_IT/demo.php +++ b/resources/lang/it_IT/demo.php @@ -25,8 +25,8 @@ declare(strict_types=1); return [ 'no_demo_text' => 'Spiacenti, non esiste un testo dimostrativo aggiuntivo per questa pagina.', 'see_help_icon' => 'Tuttavia, l\'icona in alto a destra potrebbe dirti di più.', - 'index' => 'Benvenuto in Firefly III! In questa pagina ottieni una rapida panoramica delle tue finanze. Per ulteriori informazioni, controlla Account e → Account asset e, naturalmente, i budget e i resoconti. O semplicemente dai un\'occhiata in giro e vedi dove finisci.', - 'accounts-index' => 'I conti degli asset sono i tuoi conti bancari personali. I conti spese sono gli account a cui si spendono soldi, come negozi e amici. I conti delle entrate sono conti da cui ricevi denaro, come il tuo lavoro, il governo o altre fonti di reddito. In questa pagina puoi modificarli o rimuoverli.', + 'index' => 'Benvenuto in Firefly III! In questa pagina ottieni una rapida panoramica delle tue finanze. Per ulteriori informazioni, controlla Conti → Conti attività e, naturalmente, le pagine Budget e Resoconti. O semplicemente dai un\'occhiata in giro e vedi dove finisci.', + 'accounts-index' => 'I conti attività sono i conti bancari personali. I conti spese sono i conti verso cui si spendono soldi, come negozi e amici. I conti entrate sono conti da cui ricevi denaro, come il tuo lavoro, il governo o altre fonti di reddito. In questa pagina puoi modificarli o rimuoverli.', 'budgets-index' => 'Questa pagina ti mostra una panoramica dei tuoi budget. La barra in alto mostra l\'importo disponibile per essere preventivato. Questo può essere personalizzato per qualsiasi periodo facendo clic sull\'importo a destra. La quantità che hai effettivamente speso è mostrata nella barra sottostante. Di seguito sono indicate le spese per budget e ciò che hai preventivato per loro.', 'reports-index-start' => 'Firefly III supporta un certo numero di tipi di resoconto. Leggi facendo clic sull\'icona in alto a destra.', 'reports-index-examples' => 'Assicurati di dare un occhiata a questi esempi: una panoramica finanziaria mensile , una panoramica finanziaria annuale e una panoramica del budget .', @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Queste spese, depositi e trasferimenti non sono particolarmente fantasiosi. Sono stati generati automaticamente.', 'piggy-banks-index' => 'Come puoi vedere, ci sono tre salvadanai. Utilizzare i pulsanti più e meno per influenzare la quantità di denaro in ogni salvadanaio. Fare clic sul nome del salvadanaio per visualizzare la gestione per ciascun salvadanaio.', 'import-index' => 'Qualsiasi file CSV può essere importato in Firefly III. Supporta anche l\'importazione di dati da bunq e Spectre. Altre banche e aggregatori finanziari saranno implementati in futuro. Tuttavia, come utente demo, puoi vedere solo il provider "fittizio" in azione. Genererà alcune transazioni casuali per mostrarti come funziona il processo.', + 'recurring-index' => 'Questa caratteristica è in corso di sviluppo e potrebbe non funzionare come ci si aspetta.', + 'recurring-create' => 'Questa caratteristica è in corso di sviluppo e potrebbe non funzionare come ci si aspetta.', ]; diff --git a/resources/lang/it_IT/firefly.php b/resources/lang/it_IT/firefly.php index 0532dcd126..e8ac7bec0d 100644 --- a/resources/lang/it_IT/firefly.php +++ b/resources/lang/it_IT/firefly.php @@ -33,8 +33,8 @@ return [ 'today' => 'oggi', 'customRange' => 'Range personalizzato', 'apply' => 'Applica', - 'select_date' => 'Seleziona data..', - 'cancel' => 'Cancella', + 'select_date' => 'Seleziona data...', + 'cancel' => 'Annulla', 'from' => 'Da', 'to' => 'A', 'showEverything' => 'Mostra tutto', @@ -47,7 +47,7 @@ return [ 'create_new_stuff' => 'Crea nuove cose', 'new_withdrawal' => 'Nuovo prelievo', 'create_new_transaction' => 'Crea nuova transazione', - 'go_to_asset_accounts' => 'Visualizza i tuoi movimenti', + 'go_to_asset_accounts' => 'Visualizza i tuoi conti attività', 'go_to_budgets' => 'Vai ai tuoi budget', 'go_to_categories' => 'Vai alle tue categorie', 'go_to_bills' => 'Vai alle tue bollette', @@ -57,9 +57,9 @@ return [ 'new_deposit' => 'Nuova entrata', 'new_transfer' => 'Nuovo trasferimento', 'new_transfers' => 'Nuovo trasferimento', - 'new_asset_account' => 'Nuova attività conto', - 'new_expense_account' => 'Nuova spesa conto', - 'new_revenue_account' => 'Nuova entrata conto', + 'new_asset_account' => 'Nuovo conto attività', + 'new_expense_account' => 'Nuovo conto spese', + 'new_revenue_account' => 'Nuovo conto entrate', 'new_budget' => 'Nuovo budget', 'new_bill' => 'Nuova bolletta', 'block_account_logout' => 'Sei stato disconnesso. Gli account bloccati non possono utilizzare questo sito. Ti sei registrato con un indirizzo email valido?', @@ -69,44 +69,44 @@ return [ 'flash_error' => 'Errore!', 'flash_info_multiple' => 'C\'è un messaggio | Ci sono :count messages', 'flash_error_multiple' => 'C\'è un errore | Ci sono :count errors', - 'net_worth' => 'Valore netto', + 'net_worth' => 'Patrimonio', 'route_has_no_help' => 'Non c\'è aiuto per questa rotta.', 'help_for_this_page' => 'Aiuto per questa pagina', 'no_help_could_be_found' => 'Non è stato trovato alcun testo di aiuto.', 'no_help_title' => 'Ci scusiamo, si è verificato un errore.', 'two_factor_welcome' => 'Ciao, :user!', - 'two_factor_enter_code' => 'Per continuare, inserisci il tuo codice di autenticazione a due fattori. La tua applicazione può generarlo per te.', - 'two_factor_code_here' => 'Inserisci codice qui', + 'two_factor_enter_code' => 'Per continuare inserisci il tuo codice di autenticazione a due fattori. La tua applicazione può generarlo per te.', + 'two_factor_code_here' => 'Inserisci qui il codice', 'two_factor_title' => 'Autenticazione a due fattori', - 'authenticate' => 'Autenticato', + 'authenticate' => 'Autenticati', 'two_factor_forgot_title' => 'Autenticazione a due fattori persa', 'two_factor_forgot' => 'Ho dimenticato la mia chiave a due fattori.', 'two_factor_lost_header' => 'Hai perso l\'autenticazione a due fattori?', 'two_factor_lost_intro' => 'Sfortunatamente, questo non è qualcosa che puoi resettare dall\'interfaccia web. Hai due scelte.', 'two_factor_lost_fix_self' => 'Se si esegue la propria istanza di Firefly III, controllare i log in storage/logs per istruzioni.', - 'two_factor_lost_fix_owner' => 'In caso contrario, invia un mail al proprietario del sito :site_owner e chiedi loro di ripristinare la possibilità di autenticarsi a due fattori.', + 'two_factor_lost_fix_owner' => 'In caso contrario, invia un mail al proprietario del sito, :site_owner, e chiedi loro di resettare l\'autenticazione a due fattori.', 'warning_much_data' => ':days di caricamento dei dati potrebbero richiedere un pò di tempo.', 'registered' => 'Ti sei registrato con successo!', - 'Default asset account' => 'Attività conto predefinito', - 'no_budget_pointer' => 'Sembra che tu non abbia ancora dei budget. Dovresti crearne alcuni nella pagina budget. I budget possono aiutarti a tenere traccia delle spese.', + 'Default asset account' => 'Conto attività predefinito', + 'no_budget_pointer' => 'Sembra che tu non abbia ancora dei budget. Dovresti crearne alcuni nella pagina dei budget. I budget possono aiutarti a tenere traccia delle spese.', 'Savings account' => 'Conti risparmio', 'Credit card' => 'Carta di Credito', - 'source_accounts' => 'Origine conto(i)', - 'destination_accounts' => 'Destinazione Conto(i)', + 'source_accounts' => 'Conti origine', + 'destination_accounts' => 'Conti destinazione', 'user_id_is' => 'Il tuo ID utente è :user', 'field_supports_markdown' => 'Questo campo supporta Markdown.', 'need_more_help' => 'Se hai bisogno di ulteriore aiuto con Firefly III, ti preghiamo di aprire un ticket su Github.', 'reenable_intro_text' => 'Puoi anche riattivare la guida introduttiva.', 'intro_boxes_after_refresh' => 'Le caselle di introduzione riappariranno quando si aggiorna la pagina.', 'show_all_no_filter' => 'Mostra tutte le transazioni senza raggrupparle per data.', - 'expenses_by_category' => 'Spese per Categorie', + 'expenses_by_category' => 'Spese per categorie', 'expenses_by_budget' => 'Spese per budget', - 'income_by_category' => 'Reddito per categoria', - 'expenses_by_asset_account' => 'Spese per attività conto', + 'income_by_category' => 'Entrate per categoria', + 'expenses_by_asset_account' => 'Spese per conto attività', 'expenses_by_expense_account' => 'Spese per conto spese', 'cannot_redirect_to_account' => 'Spiacente ma Firefly III non può reindirizzarti alla pagina corretta.', 'sum_of_expenses' => 'Totale spese', - 'sum_of_income' => 'Totale reddito', + 'sum_of_income' => 'Somma delle entrate', 'spent_in_specific_budget' => 'Speso nel budget ":budget"', 'sum_of_expenses_in_budget' => 'Spesa totale nel budget ":budget"', 'left_in_budget_limit' => 'Lasciato a spendere in base al budget', @@ -125,10 +125,10 @@ return [ 'multi_select_select_all' => 'Seleziona tutto', 'multi_select_n_selected' => 'selezionato', 'multi_select_all_selected' => 'Seleziona tutto', - 'multi_select_filter_placeholder' => 'Cerca..', + 'multi_select_filter_placeholder' => 'Cerca...', 'intro_next_label' => 'Avanti', 'intro_prev_label' => 'Indietro', - 'intro_skip_label' => 'Salta', + 'intro_skip_label' => 'Salta ogni', 'intro_done_label' => 'Fatto', 'between_dates_breadcrumb' => 'Fra :start e :end', 'all_journals_without_budget' => 'Tutte le transazioni senza un budget', @@ -147,19 +147,19 @@ return [ 'all_transfers' => 'Tutti i trasferimenti', 'title_transfers_between' => 'Tutti i trasferimenti fra :start e :end', 'all_transfer' => 'Tutti i trasferimenti', - 'all_journals_for_tag' => 'Tutte le transazioni per Etichetta ":tag"', + 'all_journals_for_tag' => 'Tutte le transazioni per l\'etichetta ":tag"', 'title_transfer_between' => 'Tutti i trasferimenti fra :start e :end', 'all_journals_for_category' => 'Turre le transazioni per categoria :name', 'all_journals_for_budget' => 'Tutte le transazione per budget :name', 'chart_all_journals_for_budget' => 'Grafico di tutte le transazioni per budget :name', 'journals_in_period_for_category' => 'Tutte le transazioni per Categoria :name fra :start e :end', - 'journals_in_period_for_tag' => 'Tutte le transazioni per Etichetta :tag fra :start e :end', + 'journals_in_period_for_tag' => 'Tutte le transazioni per l\'etichetta :tag fra :start e :end', 'not_available_demo_user' => 'La funzione a cui tenti di accedere non è disponibile per gli utenti demo.', - 'exchange_rate_instructions' => 'Conto attività "@name" accetta solo transazioni in @native_currency. Se invece desideri utilizzare @foreign_currency, assicurati che anche l\'importo in @native_currency sia noto:', - 'transfer_exchange_rate_instructions' => 'Il conto attività di origine "@source_name" accetta solo transazioni in @source_currency.Il conto attività di destinazione "@dest_name" accetta solo transazioni in @dest_currency. È necessario fornire l\'importo trasferito correttamente in entrambe le valute.', + 'exchange_rate_instructions' => 'Il conto attività "@name" accetta solo transazioni in @native_currency. Se invece desideri utilizzare @foreign_currency, assicurati che anche l\'importo in @native_currency sia noto:', + 'transfer_exchange_rate_instructions' => 'Il conto attività di origine "@source_name" accetta solo transazioni in @source_currency. Il conto attività di destinazione "@dest_name" accetta solo transazioni in @dest_currency. È necessario fornire l\'importo trasferito correttamente in entrambe le valute.', 'transaction_data' => 'Data Transazione', - 'invalid_server_configuration' => 'Configurazione Server non corretta', - 'invalid_locale_settings' => 'Firefly III non è in grado di formattare importi monetari perché al server mancano i pacchetti richiesti. Ci sono istruzioni su come eseguire questa operazione.', + 'invalid_server_configuration' => 'Configurazione del server non corretta', + 'invalid_locale_settings' => 'Firefly III non è in grado di formattare gli importi monetari perché al server mancano i pacchetti richiesti. Ci sono istruzioni su come eseguire questa operazione.', 'quickswitch' => 'Interruttore veloce', 'sign_in_to_start' => 'Accedi per iniziare la sessione', 'sign_in' => 'Accedi', @@ -189,14 +189,14 @@ return [ 'check_for_updates_permission' => 'Firefly III può controllare gli aggiornamenti, ma è necessario il tuo permesso per farlo. Vai alla amministrazione per indicare se desideri che questa funzione sia abilitata.', 'updates_ask_me_later' => 'Chiedimelo più tardi', 'updates_do_not_check' => 'Non controllare gli aggiornamenti', - 'updates_enable_check' => 'Abilita il controllo per gli aggiornamenti', + 'updates_enable_check' => 'Abilita il controllo degli aggiornamenti', 'admin_update_check_now_title' => 'Controlla gli aggiornamenti ora', - 'admin_update_check_now_explain' => 'Se si preme il pulsante, Firefly III vedrà se la versione corrente è la più recente.', + 'admin_update_check_now_explain' => 'Se si preme il pulsante, Firefly III controllerà se la versione corrente è la più recente.', 'check_for_updates_button' => 'Controlla ora!', 'update_new_version_alert' => 'È disponibile una nuova versione di Firefly III. Stai eseguendo v:your_version, l\'ultima versione è v:new_version che è stata rilasciata :date.', 'update_current_version_alert' => 'Stai eseguendo v:version, che è l\'ultima versione disponibile.', 'update_newer_version_alert' => 'Stai eseguendo v:your_version, che è più recente rispetto all\'ultima versione, v:new_version.', - 'update_check_error' => 'Si è verificato un errore durante il controllo degli aggiornamenti. Si prega di visualizzare i file di registro.', + 'update_check_error' => 'Si è verificato un errore durante il controllo degli aggiornamenti. Si prega di visualizzare i file di log.', // search 'search' => 'Cerca', @@ -206,15 +206,15 @@ return [ 'search_box' => 'Ricerca', 'search_box_intro' => 'Benvenuto nella funzione di ricerca di Firefly III. Inserisci la query di ricerca nella casella. Assicurati di controllare il file della guida perché la ricerca è abbastanza avanzata.', 'search_error' => 'Errore durante la ricerca', - 'search_searching' => 'Ricerca ...', + 'search_searching' => 'Ricerca in corso...', 'search_results' => 'Risultati ricerca', // repeat frequencies: - 'repeat_freq_yearly' => 'annuale', + 'repeat_freq_yearly' => 'annualmente', 'repeat_freq_half-year' => 'ogni sei mesi', - 'repeat_freq_quarterly' => 'trimestrale', - 'repeat_freq_monthly' => 'mensile', - 'repeat_freq_weekly' => 'settimanale', + 'repeat_freq_quarterly' => 'trimestralmente', + 'repeat_freq_monthly' => 'mensilmente', + 'repeat_freq_weekly' => 'settimanalmente', 'weekly' => 'settimanale', 'quarterly' => 'trimestrale', 'half-year' => 'ogni sei mesi', @@ -224,71 +224,71 @@ return [ 'import_and_export' => 'Importa e esporta', 'export_data' => 'Esporta dati', 'export_and_backup_data' => 'Esporta dati', - 'export_data_intro' => 'Utilizzare i dati esportati per passare a una nuova applicazione finanziaria. Si noti che questi file non sono intesi come backup. Non contengono abbastanza metadati per ripristinare completamente una nuova installazione di Firefly III. Se si desidera eseguire un backup dei dati, eseguire direttamente il backup del database.', - 'export_format' => 'Esporta formato', + 'export_data_intro' => 'Utilizza i dati esportati per passare a una nuova applicazione finanziaria. Si noti che questi file non sono intesi come backup. Non contengono abbastanza metadati per ripristinare completamente una nuova installazione di Firefly III. Se desideri eseguire un backup dei dati, esegui direttamente il backup del database.', + 'export_format' => 'Formato esportazione', 'export_format_csv' => 'Valori separati da virgola (file CSV)', 'export_format_mt940' => 'Formato compatibile MT940', 'include_old_uploads_help' => 'Firefly III non getta via i file CSV originali importati in passato. Puoi includerli nell\'esportazione.', 'do_export' => 'Esporta', 'export_status_never_started' => 'L\'esportazione non è ancora iniziata', - 'export_status_make_exporter' => 'Creare una cosa esportatore...', + 'export_status_make_exporter' => 'Creazione esportatore...', 'export_status_collecting_journals' => 'Raccolta delle tue transazioni...', 'export_status_collected_journals' => 'Raccolto le tue transazioni!', - 'export_status_converting_to_export_format' => 'Convertire le tue transazioni...', + 'export_status_converting_to_export_format' => 'Conversione delle tue transazioni...', 'export_status_converted_to_export_format' => 'Convertite le vostre transazioni!', - 'export_status_creating_journal_file' => 'Creare il file di esportazione...', - 'export_status_created_journal_file' => 'Creato il file di esportazione!', - 'export_status_collecting_attachments' => 'Raccogli tutti i tuoi allegati...', + 'export_status_creating_journal_file' => 'Creazione del file di esportazione...', + 'export_status_created_journal_file' => 'File di esportazione creato!', + 'export_status_collecting_attachments' => 'Raccolta di tutti i tuoi allegati...', 'export_status_collected_attachments' => 'Raccolti tutti i tuoi allegati!', - 'export_status_collecting_old_uploads' => 'Raccogli tutti i tuoi caricamenti precedenti...', + 'export_status_collecting_old_uploads' => 'Raccolta di tutti i tuoi caricamenti precedenti...', 'export_status_collected_old_uploads' => 'Raccolti tutti i tuoi caricamenti precedenti!', - 'export_status_creating_zip_file' => 'Creare un file zip...', + 'export_status_creating_zip_file' => 'Creazione di un file zip...', 'export_status_created_zip_file' => 'File zip creato!', - 'export_status_finished' => 'Esportazione terminata correttamente!!', + 'export_status_finished' => 'L\'esportazione è terminata con successo! Evviva!', 'export_data_please_wait' => 'Attendere prego...', // rules 'rules' => 'Regole', 'rule_name' => 'Nome regola', 'rule_triggers' => 'La regola si innesca quando', - 'rule_actions' => 'La regola lo farà', + 'rule_actions' => 'La regola eseguirà', 'new_rule' => 'Nuova regola', 'new_rule_group' => 'Nuovo gruppo di regole', - 'rule_priority_up' => 'Dare maggiore priorità alla regola', - 'rule_priority_down' => 'Dare alla regola meno priorità', + 'rule_priority_up' => 'Dai maggiore priorità alla regola', + 'rule_priority_down' => 'Dai minore priorità alla regola', 'make_new_rule_group' => 'Crea un nuovo gruppo di regole', 'store_new_rule_group' => 'Memorizza un nuovo gruppo di regole', 'created_new_rule_group' => 'Nuovo gruppo di regole ":title" memorizzate!', - 'updated_rule_group' => 'Gruppo di regole aggiornato con successo ":title".', + 'updated_rule_group' => 'Gruppo di regole ":title" aggiornato con successo.', 'edit_rule_group' => 'Modifica il gruppo di regole ":title"', 'delete_rule_group' => 'Elimina il gruppo di regole ":title"', 'deleted_rule_group' => 'Gruppo regole eliminato ":title"', 'update_rule_group' => 'Aggiorna gruppo di regole', 'no_rules_in_group' => 'Non ci sono regole in questo gruppo', - 'move_rule_group_up' => 'Sposta il gruppo di regole su', - 'move_rule_group_down' => 'Sposta il gruppo di regole in basso', - 'save_rules_by_moving' => 'Salva questa(e) regola(e) spostandola(e) in un altro gruppo di regole:', + 'move_rule_group_up' => 'Sposta sopra il gruppo di regole', + 'move_rule_group_down' => 'Sposta sotto il gruppo di regole', + 'save_rules_by_moving' => 'Salva queste regole spostandole in un altro gruppo di regole:', 'make_new_rule' => 'Crea una nuova regola nel gruppo di regole ":title"', 'rule_is_strict' => 'regola severa', 'rule_is_not_strict' => 'regola non severa', 'rule_help_stop_processing' => 'Quando selezioni questa casella, le regole successive in questo gruppo non verranno eseguite.', 'rule_help_strict' => 'Nelle regole severe TUTTI i trigger devono venire azionati perché l\'azione venga eseguita. Nelle regole non severe, è sufficiente UN QUALSIASI trigger perché l\'azione venga eseguita.', - 'rule_help_active' => 'Le regole non attive non spareranno mai.', + 'rule_help_active' => 'Le regole non attive non verranno mai eseguite.', 'stored_new_rule' => 'Nuova regola memorizzata con titolo ":title"', 'deleted_rule' => 'Regola eliminata con titolo ":title"', 'store_new_rule' => 'Salva nuova regola', 'updated_rule' => 'Regola aggiornata con titolo ":title"', 'default_rule_group_name' => 'Regole predefinite', - 'default_rule_group_description' => 'Tutte le tue regole non in un gruppo particolare.', + 'default_rule_group_description' => 'Tutte le tue regole che non sono in un gruppo specifico.', 'default_rule_name' => 'La tua prima regola predefinita', - 'default_rule_description' => 'Questa regola è un esempio. Puoi tranquillamente cancellarla.', + 'default_rule_description' => 'Questa regola è un esempio. Puoi tranquillamente eliminarla.', 'default_rule_trigger_description' => 'L\'uomo che vendette il mondo', 'default_rule_trigger_from_account' => 'David Bowie', 'default_rule_action_prepend' => 'Comprato il mondo da ', 'default_rule_action_set_category' => 'Grandi spese', 'trigger' => 'Trigger', 'trigger_value' => 'Attiva al valore', - 'stop_processing_other_triggers' => 'Interrompi l\'elaborazione di altri trigger', + 'stop_processing_other_triggers' => 'Smetti di elaborare altri trigger', 'add_rule_trigger' => 'Aggiungi un nuovo trigger', 'action' => 'Azione', 'action_value' => 'Valore azione', @@ -332,19 +332,19 @@ return [ 'rule_trigger_transaction_type' => 'La transazione è di tipo ":trigger_value"', 'rule_trigger_category_is_choice' => 'La categoria è...', 'rule_trigger_category_is' => 'La categoria è ":trigger_value"', - 'rule_trigger_amount_less_choice' => 'L\'importo è inferiore a..', + 'rule_trigger_amount_less_choice' => 'L\'importo è inferiore a...', 'rule_trigger_amount_less' => 'L\'importo è inferiore a :trigger_value', - 'rule_trigger_amount_exactly_choice' => 'L\'importo è..', + 'rule_trigger_amount_exactly_choice' => 'L\'importo è...', 'rule_trigger_amount_exactly' => 'L\'importo è :trigger_value', - 'rule_trigger_amount_more_choice' => 'L\'importo è più di..', + 'rule_trigger_amount_more_choice' => 'L\'importo è più di...', 'rule_trigger_amount_more' => 'L\'importo è più di :trigger_value', - 'rule_trigger_description_starts_choice' => 'La descrizione inizia con..', + 'rule_trigger_description_starts_choice' => 'La descrizione inizia con...', 'rule_trigger_description_starts' => 'La descrizione inizia con ":trigger_value"', - 'rule_trigger_description_ends_choice' => 'La descrizione termina con..', + 'rule_trigger_description_ends_choice' => 'La descrizione termina con...', 'rule_trigger_description_ends' => 'La descrizione termina con ":trigger_value"', - 'rule_trigger_description_contains_choice' => 'La descrizione contiene..', + 'rule_trigger_description_contains_choice' => 'La descrizione contiene...', 'rule_trigger_description_contains' => 'La descrizione contiene ":trigger_value"', - 'rule_trigger_description_is_choice' => 'La descrizione è..', + 'rule_trigger_description_is_choice' => 'La descrizione è...', 'rule_trigger_description_is' => 'La descrizione è ":trigger_value"', 'rule_trigger_budget_is_choice' => 'Il budget è...', 'rule_trigger_budget_is' => 'Il budget è ":trigger_value"', @@ -352,8 +352,8 @@ return [ 'rule_trigger_tag_is' => 'Una etichetta è ":trigger_value"', 'rule_trigger_currency_is_choice' => 'La valuta della transazione è...', 'rule_trigger_currency_is' => 'La valuta della transazione è ":trigger_value"', - 'rule_trigger_has_attachments_choice' => 'Ha almeno questo molti allegati', - 'rule_trigger_has_attachments' => 'Almeno :trigger_value allegato (i)', + 'rule_trigger_has_attachments_choice' => 'Ha almeno così tanti allegati', + 'rule_trigger_has_attachments' => 'Ha almeno :trigger_value allegati', 'rule_trigger_store_journal' => 'Quando viene creata una transazione', 'rule_trigger_update_journal' => 'Quando una transazione viene aggiornata', 'rule_trigger_has_no_category_choice' => 'Non ha categoria', @@ -364,53 +364,53 @@ return [ 'rule_trigger_has_no_budget' => 'La transazione non ha un budget', 'rule_trigger_has_any_budget_choice' => 'Ha un (qualsiasi) budget', 'rule_trigger_has_any_budget' => 'La transazione ha un (qualsiasi) budget', - 'rule_trigger_has_no_tag_choice' => 'Non ha etichetta(e)', - 'rule_trigger_has_no_tag' => 'La transazione non ha etichetta(e)', + 'rule_trigger_has_no_tag_choice' => 'Non ha etichette', + 'rule_trigger_has_no_tag' => 'La transazione non ha etichette', 'rule_trigger_has_any_tag_choice' => 'Ha una o più etichette (qualsiasi)', - 'rule_trigger_has_any_tag' => 'La transazione ha una (qualsiasi) o più etichette', - 'rule_trigger_any_notes_choice' => 'Ha (qualsiasi) note', - 'rule_trigger_any_notes' => 'La transazione ha (qualsiasi) note', + 'rule_trigger_has_any_tag' => 'La transazione ha una o più etichette (qualsiasi)', + 'rule_trigger_any_notes_choice' => 'Ha una (qualsiasi) nota', + 'rule_trigger_any_notes' => 'La transazione ha una (qualsiasi) nota', 'rule_trigger_no_notes_choice' => 'Non ha note', 'rule_trigger_no_notes' => 'La transazione non ha note', - 'rule_trigger_notes_are_choice' => 'Le note sono..', + 'rule_trigger_notes_are_choice' => 'Le note sono...', 'rule_trigger_notes_are' => 'Le note sono ":trigger_value"', - 'rule_trigger_notes_contain_choice' => 'Le note contengono..', + 'rule_trigger_notes_contain_choice' => 'Le note contengono...', 'rule_trigger_notes_contain' => 'Le note contengono ":trigger_value"', - 'rule_trigger_notes_start_choice' => 'Le note iniziano con..', + 'rule_trigger_notes_start_choice' => 'Le note iniziano con...', 'rule_trigger_notes_start' => 'Le note iniziano con ":trigger_value"', - 'rule_trigger_notes_end_choice' => 'Le note finiscono con..', + 'rule_trigger_notes_end_choice' => 'Le note finiscono con...', 'rule_trigger_notes_end' => 'Le note finiscono con ":trigger_value"', 'rule_action_set_category' => 'Imposta categoria a ":action_value"', - 'rule_action_clear_category' => 'Cancella categoria', + 'rule_action_clear_category' => 'Rimuovi dalla categoria', 'rule_action_set_budget' => 'Imposta il budget su ":action_value"', - 'rule_action_clear_budget' => 'Cancella budget', + 'rule_action_clear_budget' => 'Rimuovi dal budget', 'rule_action_add_tag' => 'Aggiungi etichetta ":action_value"', - 'rule_action_remove_tag' => 'Rimuovi etichetta ":action_value"', + 'rule_action_remove_tag' => 'Rimuovi l\'etichetta ":action_value"', 'rule_action_remove_all_tags' => 'Rimuovi tutte le etichette', 'rule_action_set_description' => 'Imposta la descrizione a ":action_value"', - 'rule_action_append_description' => 'Aggiungi descrizione con ":action_value"', - 'rule_action_prepend_description' => 'Anteporre descrizione con ":action_value"', - 'rule_action_set_category_choice' => 'Imposta categoria a..', - 'rule_action_clear_category_choice' => 'Cancella qualsiasi categoria', + 'rule_action_append_description' => 'Aggiungi alla descrizione ":action_value"', + 'rule_action_prepend_description' => 'Anteponi alla descrizione ":action_value"', + 'rule_action_set_category_choice' => 'Imposta come categoria...', + 'rule_action_clear_category_choice' => 'Rimuovi da tutte le categorie', 'rule_action_set_budget_choice' => 'Imposta il budget su...', - 'rule_action_clear_budget_choice' => 'Cancella qualsiasi budget', - 'rule_action_add_tag_choice' => 'Aggiungi etichetta..', - 'rule_action_remove_tag_choice' => 'Rimuovi etichetta..', + 'rule_action_clear_budget_choice' => 'Rimuovi da tutti i budget', + 'rule_action_add_tag_choice' => 'Aggiungi l\'etichetta...', + 'rule_action_remove_tag_choice' => 'Rimuovi l\'etichetta...', 'rule_action_remove_all_tags_choice' => 'Rimuovi tutte le etichette', - 'rule_action_set_description_choice' => 'Imposta la descrizione a..', - 'rule_action_append_description_choice' => 'Aggiungi descrizione con..', - 'rule_action_prepend_description_choice' => 'Anteporre descrizione..', - 'rule_action_set_source_account_choice' => 'Imposta l\'account di origine su...', - 'rule_action_set_source_account' => 'Imposta l\'account di origine su :action_value', - 'rule_action_set_destination_account_choice' => 'Imposta l\'account di destinazione su...', - 'rule_action_set_destination_account' => 'Imposta l\'account di destinazione su :action_value', - 'rule_action_append_notes_choice' => 'Aggiungi note con..', - 'rule_action_append_notes' => 'Aggiungi note con ":action_value"', - 'rule_action_prepend_notes_choice' => 'Anteponi note con..', - 'rule_action_prepend_notes' => 'Anteponi note con ":action_value"', - 'rule_action_clear_notes_choice' => 'Rimuovi eventuali note', - 'rule_action_clear_notes' => 'Rimuovi eventuali note', - 'rule_action_set_notes_choice' => 'Imposta le note su..', + 'rule_action_set_description_choice' => 'Imposta come descrizione...', + 'rule_action_append_description_choice' => 'Aggiungi alla descrizione...', + 'rule_action_prepend_description_choice' => 'Anteponi alla descrizione...', + 'rule_action_set_source_account_choice' => 'Imposta come conto di origine...', + 'rule_action_set_source_account' => 'Imposta come conto di origine :action_value', + 'rule_action_set_destination_account_choice' => 'Imposta come conto di destinazione...', + 'rule_action_set_destination_account' => 'Imposta come conto di destinazione :action_value', + 'rule_action_append_notes_choice' => 'Aggiungi alle note...', + 'rule_action_append_notes' => 'Aggiungi alle note ":action_value"', + 'rule_action_prepend_notes_choice' => 'Anteponi alle note...', + 'rule_action_prepend_notes' => 'Anteponi alle note ":action_value"', + 'rule_action_clear_notes_choice' => 'Rimuovi tutte le note', + 'rule_action_clear_notes' => 'Rimuovi tutte le note', + 'rule_action_set_notes_choice' => 'Imposta come note...', 'rule_action_link_to_bill_choice' => 'Collega ad una bolletta...', 'rule_action_link_to_bill' => 'Collegamento alla bolletta ":action_value"', 'rule_action_set_notes' => 'Imposta le note su ":action_value"', @@ -422,7 +422,7 @@ return [ 'rule_for_bill_title' => 'Regole generata automaticamente per la bolletta ":name"', 'rule_for_bill_description' => 'Questa regola è generata automaticamente per l\'abbinamento con la bolletta ":name".', 'create_rule_for_bill' => 'Crea una nuova regola per la bolletta ":name"', - 'create_rule_for_bill_txt' => 'Contratulazioni, hai appena creato una nuova bolletta chiamata ":name"! Firefly III può automagicamente abbinare le nuove uscite a questa bolletta. Per esempio, ogni volta che paghi l\'affitto la bolletta "affitto" verrà collegata a questa spesa. In questo modo Firefly III può visualizzare con accuratezza quali bollette sono in scadenza e quali no. Per far ciò è necessario creare una nuova regola. Firefly III ha inserito al posto tuo alcuni dettagli ragionevoli. Assicurati che questi siano corretti. Se questi valori sono corretti, Firefly III automaticamente collegherà il prelievo giusto alla bolletta giusta. Controlla che i trigger siano corretti e aggiungene altri se sono sbagliati.', + 'create_rule_for_bill_txt' => 'Congratulazioni, hai appena creato una nuova bolletta chiamata ":name"! Firefly III può automagicamente abbinare le nuove uscite a questa bolletta. Per esempio, ogni volta che paghi l\'affitto la bolletta "affitto" verrà collegata a questa spesa. In questo modo Firefly III può visualizzare con accuratezza quali bollette sono in scadenza e quali no. Per far ciò è necessario creare una nuova regola. Firefly III ha inserito al posto tuo alcuni dettagli ragionevoli. Assicurati che questi siano corretti. Se questi valori sono corretti, Firefly III automaticamente collegherà il prelievo giusto alla bolletta giusta. Controlla che i trigger siano corretti e aggiungine altri se sono sbagliati.', 'new_rule_for_bill_title' => 'Regola per la bolletta ":name"', 'new_rule_for_bill_description' => 'Questa regola contrassegna le transazioni per la bolletta ":name".', @@ -437,16 +437,16 @@ return [ 'sums_apply_to_range' => 'Tutte le somme si applicano all\'intervallo selezionato', 'mapbox_api_key' => 'Per utilizzare la mappa, ottieni una chiave API da Mapbox. Apri il tuo file .env e inserisci questo codice dopo MAPBOX_API_KEY=.', 'press_tag_location' => 'Fai clic destro o premi a lungo per impostare la posizione dell\'erichetta.', - 'clear_location' => 'Cancella posizione', + 'clear_location' => 'Rimuovi dalla posizione', // preferences - 'pref_home_screen_accounts' => 'Conti nella schermata iniziale', - 'pref_home_screen_accounts_help' => 'Quali conti dovrebbero essere visualizzati sulla home page?', - 'pref_view_range' => 'Visualizza gamma', + 'pref_home_screen_accounts' => 'Conti nella pagina iniziale', + 'pref_home_screen_accounts_help' => 'Quali conti vuoi che vengano visualizzati nella pagina principale?', + 'pref_view_range' => 'Intervallo di visualizzazione', 'pref_view_range_help' => 'Alcuni grafici sono raggruppati automaticamente in periodi. Che periodo preferiresti?', - 'pref_1D' => '1 Giorno', - 'pref_1W' => '1 Settimana', - 'pref_1M' => '1 Mese', + 'pref_1D' => 'Un giorno', + 'pref_1W' => 'Una settimana', + 'pref_1M' => 'Un mese', 'pref_3M' => 'Tre mesi (trimestre)', 'pref_6M' => 'Sei mesi', 'pref_1Y' => 'Un anno', @@ -454,10 +454,10 @@ return [ 'pref_languages_help' => 'Firefly III supporta diverse lingue.', 'pref_custom_fiscal_year' => 'Impostazioni anno fiscale', 'pref_custom_fiscal_year_label' => 'Abilita', - 'pref_custom_fiscal_year_help' => 'Nei paesi che utilizzano un anno finanziario diverso dal 1 ° gennaio al 31 dicembre, è possibile attivarlo e specificare i giorni di inizio / fine anno fiscale', + 'pref_custom_fiscal_year_help' => 'Nei paesi che utilizzano un anno finanziario diverso rispetto al dal 1 gennaio al 31 dicembre, è possibile attivarlo e specificare i giorni di inizio / fine dell\'anno fiscale', 'pref_fiscal_year_start_label' => 'Data di inizio anno fiscale', 'pref_two_factor_auth' => 'Verifica in due passaggi', - 'pref_two_factor_auth_help' => 'Quando abiliti la verifica in due passaggi (nota anche come autenticazione a due fattori), aggiungi un ulteriore livello di sicurezza al tuo account. Accedi con qualcosa che conosci (la tua password) e qualcosa che hai (un codice di verifica). I codici di verifica sono generati da un\'applicazione sul telefono, come Authy o Autenticatore Google.', + 'pref_two_factor_auth_help' => 'Quando abiliti la verifica a due passaggi (nota anche come autenticazione a due fattori), aggiungi un ulteriore livello di sicurezza al tuo account. Accedi con qualcosa che conosci (la tua password) e qualcosa che hai (un codice di verifica). I codici di verifica sono generati da un\'applicazione sul telefono, come Authy o Google Authenticator.', 'pref_enable_two_factor_auth' => 'Abilita la verifica in due passaggi', 'pref_two_factor_auth_disabled' => 'Codice di verifica in due passaggi rimosso e disabilitato', 'pref_two_factor_auth_remove_it' => 'Non dimenticare di rimuovere l\'account dalla tua app di autenticazione!', @@ -465,18 +465,19 @@ return [ 'pref_two_factor_auth_code_help' => 'Esegui la scansione del codice QR con un\'applicazione sul tuo telefono come Authy o Google Authenticator e inserisci il codice generato.', 'pref_two_factor_auth_reset_code' => 'Reimposta il codice di verifica', 'pref_two_factor_auth_disable_2fa' => 'Disattiva 2FA', + '2fa_use_secret_instead' => 'Se non puoi scansionare il codice QR puoi utilizzare il segreto: :secret.', 'pref_save_settings' => 'Salva le impostazioni', 'saved_preferences' => 'Preferenze salvate!', 'preferences_general' => 'Generale', - 'preferences_frontpage' => 'Schermata Home', + 'preferences_frontpage' => 'Pagina principale', 'preferences_security' => 'Sicurezza', 'preferences_layout' => 'Impaginazione', - 'pref_home_show_deposits' => 'Mostra i depositi sulla schermata principale', - 'pref_home_show_deposits_info' => 'La schermata iniziale mostra già i tuoi conti spese. Dovrebbe mostrare anche i tuoi account delle entrate?', + 'pref_home_show_deposits' => 'Mostra i depositi nella pagina principale', + 'pref_home_show_deposits_info' => 'La pagina iniziale mostra già i tuoi conti spese. Vuoi che mostri anche i tuoi conti entrate?', 'pref_home_do_show_deposits' => 'Sì, mostrali', 'successful_count' => 'di cui :count con successo', 'list_page_size_title' => 'Dimensioni pagina', - 'list_page_size_help' => 'Qualsiasi elenco di cose (conti, transazioni, ecc.) Mostra al massimo questo numero per pagina.', + 'list_page_size_help' => 'Ogni elenco (di conti, di transazioni, ecc) mostra al massimo questo numero di elementi per pagina.', 'list_page_size_label' => 'Dimensioni pagina', 'between_dates' => '(:start e :end)', 'pref_optional_fields_transaction' => 'Campi opzionali per le transazioni', @@ -484,12 +485,12 @@ return [ 'optional_tj_date_fields' => 'Campi data', 'optional_tj_business_fields' => 'Campi aziendali', 'optional_tj_attachment_fields' => 'Campi allegati', - 'pref_optional_tj_interest_date' => 'Data di interesse', - 'pref_optional_tj_book_date' => 'Data del libro', - 'pref_optional_tj_process_date' => 'Data di lavorazione', - 'pref_optional_tj_due_date' => 'Scadenza', - 'pref_optional_tj_payment_date' => 'Data di pagamento', - 'pref_optional_tj_invoice_date' => 'Data bolletta', + 'pref_optional_tj_interest_date' => 'Data interessi', + 'pref_optional_tj_book_date' => 'Data contabile', + 'pref_optional_tj_process_date' => 'Data elaborazione', + 'pref_optional_tj_due_date' => 'Data scadenza', + 'pref_optional_tj_payment_date' => 'Data pagamento', + 'pref_optional_tj_invoice_date' => 'Data fatturazione', 'pref_optional_tj_internal_reference' => 'Riferimento interno', 'pref_optional_tj_notes' => 'Note', 'pref_optional_tj_attachments' => 'Allegati', @@ -503,9 +504,9 @@ return [ 'delete_account' => 'Elimina account', 'current_password' => 'Password corrente', 'new_password' => 'Nuova password', - 'new_password_again' => 'Nuova password (ancora)', - 'delete_your_account' => 'cancella il tuo account', - 'delete_your_account_help' => 'L\'eliminazione del tuo account eliminerà anche conti, transazioni, qualsiasi cosa che potresti aver salvato in Firefly III. Sarà ELIMINATO.', + 'new_password_again' => 'Nuova password (ripeti)', + 'delete_your_account' => 'Elimina il tuo account', + 'delete_your_account_help' => 'L\'eliminazione del tuo account eliminerà anche conti, transazioni, qualsiasi cosa che potresti aver salvato in Firefly III. Sarà tutto PERDUTO.', 'delete_your_account_password' => 'Inserisci la tua password per continuare.', 'password' => 'Password', 'are_you_sure' => 'Sei sicuro? Non puoi annullare questo.', @@ -516,20 +517,20 @@ return [ 'invalid_password' => 'Password non valida!', 'what_is_pw_security' => 'Che cos\'è "verifica la sicurezza della password"?', 'secure_pw_title' => 'Come scegliere una password sicura', - 'secure_pw_history' => 'Nell\'agosto 2017, il noto ricercatore di sicurezza Troy Hunt ha pubblicato una lista di 306 milioni di password rubate. Queste password sono state rubate durante i break in aziende come LinkedIn, Adobe e NeoPets (e molte altre).', + 'secure_pw_history' => 'Nell\'agosto 2017 il noto ricercatore di sicurezza Troy Hunt ha pubblicato una lista di 306 milioni di password rubate. Queste password sono state rubate durante incursioni in aziende come LinkedIn, Adobe e NeoPets (e molte altre).', 'secure_pw_check_box' => 'Selezionando la casella, Firefly III invierà l\'hash SHA1 della tua password a il sito web di Troy Hunt per vedere se è presente nell\'elenco. Questo ti impedirà di usare password non sicure come raccomandato nell\'ultima Pubblicazione speciale NIST su questo argomento.', 'secure_pw_sha1' => 'Ma pensavo che SHA1 fosse rotto?', 'secure_pw_hash_speed' => 'Sì, ma non in questo contesto. Come puoi leggere su il sito web che spiega come hanno rotto SHA1 , ora è leggermente più facile trovare una "collisione": un\'altra stringa che risulta nello stesso hash SHA1. Ora ci vogliono solo 10.000 anni usando una macchina a GPU singola.', - 'secure_pw_hash_security' => 'Questa collisione non sarebbe uguale alla tua password, né sarebbe utile su (un sito come) Firefly III. Questa applicazione non utilizza SHA1 per la verifica della password. Quindi è sicuro controllare questa casella. La tua password viene sottoposta a hash e inviata tramite HTTPS.', + 'secure_pw_hash_security' => 'Questa collisione non sarebbe uguale alla tua password, né sarebbe utile su (un sito come) Firefly III. Questa applicazione non utilizza SHA1 per la verifica della password. Quindi è sicuro selezionare questa casella. La tua password viene sottoposta a hash e solo i primi cinque caratteri di questo hash vengono inviati tramite HTTPS.', 'secure_pw_should' => 'Devo controllare la scatola?', 'secure_pw_long_password' => 'Se hai appena generato una password lunga e monouso per Firefly III utilizzando un qualche tipo di generatore di password: no.', 'secure_pw_short' => 'Se hai appena inserito la password, usi sempre: Si prega di.', 'command_line_token' => 'Token della riga di comando', - 'explain_command_line_token' => 'È necessario questo token per eseguire le opzioni della riga di comando, come l\'importazione o l\'esportazione di dati. Senza di esso, tali comandi sensibili non funzioneranno. Non condividere il token della riga di comando. Nessuno ti chiederà questo segno, nemmeno io. Se temi di aver perso questo, o quando sei insicuro, rigenera questo token usando il pulsante.', + 'explain_command_line_token' => 'È necessario questo token per eseguire le opzioni dalla riga di comando, come l\'importazione o l\'esportazione di dati. Senza di esso tali comandi sensibili non funzioneranno. Non condividere il token della riga di comando. Nessuno ti chiederà questo token, nemmeno io. Se temi di averlo perso, o se sei paranoico, rigenera questo token usando il pulsante.', 'regenerate_command_line_token' => 'Rigenera il token della riga di comando', 'token_regenerated' => 'È stato generato un nuovo token della riga di comando', 'change_your_email' => 'Cambia il tuo indirizzo email', - 'email_verification' => 'Un messaggio di posta elettronica verrà inviato al tuo vecchio e nuovo indirizzo email. Per motivi di sicurezza, non potrai accedere fino a quando non avrai verificato il tuo nuovo indirizzo email. Se non si è sicuri che l\'installazione di Firefly III sia in grado di inviare e-mail, si prega di non utilizzare questa funzione. Se sei un amministratore, puoi verificarlo nella amministrazione.', + 'email_verification' => 'Un messaggio di posta elettronica verrà inviato al vecchio E al nuovo indirizzo email. Per motivi di sicurezza, non potrai accedere fino a quando non avrai verificato il tuo nuovo indirizzo email. Se non si è sicuri che l\'installazione di Firefly III sia in grado di inviare e-mail, si prega di non utilizzare questa funzione. Se sei un amministratore, puoi verificarlo in Amministrazione.', 'email_changed_logout' => 'Fino a quando non verifichi il tuo indirizzo email, non puoi effettuare il login.', 'login_with_new_email' => 'Ora puoi accedere con il tuo nuovo indirizzo email.', 'login_with_old_email' => 'Ora puoi accedere nuovamente con il tuo vecchio indirizzo email.', @@ -541,15 +542,15 @@ return [ 'update_attachment' => 'Aggiorna allegati', 'delete_attachment' => 'Elimina allegato ":name"', 'attachment_deleted' => 'Allegato eliminato ":name"', - 'attachment_updated' => 'Allegato aggiornato ":name"', + 'attachment_updated' => 'Allegato ":name" aggiornato', 'upload_max_file_size' => 'Dimensione massima del file: :size', 'list_all_attachments' => 'Lista di tutti gli allegati', // transaction index 'title_expenses' => 'Spese', 'title_withdrawal' => 'Spese', - 'title_revenue' => 'Reddito / Entrata', - 'title_deposit' => 'Reddito / Entrata', + 'title_revenue' => 'Entrate', + 'title_deposit' => 'Redditi / entrate', 'title_transfer' => 'Trasferimenti', 'title_transfers' => 'Trasferimenti', @@ -573,19 +574,19 @@ return [ 'convert_Transfer_to_deposit' => 'Converti questo trasferimento in un deposito', 'convert_Transfer_to_withdrawal' => 'Converti questo trasferimento in un prelievo', 'convert_please_set_revenue_source' => 'Si prega di scegliere il conto delle entrate da dove verranno i soldi.', - 'convert_please_set_asset_destination' => 'Si prega di scegliere il conto patrimoniale dove andranno i soldi.', + 'convert_please_set_asset_destination' => 'Scegli il conto attività in cui andranno i soldi.', 'convert_please_set_expense_destination' => 'Si prega di scegliere il conto spese dove andranno i soldi.', - 'convert_please_set_asset_source' => 'Si prega di scegliere il conto patrimoniale da dove verranno i soldi.', + 'convert_please_set_asset_source' => 'Scegli il conto attività da cui verranno i soldi.', 'convert_explanation_withdrawal_deposit' => 'Se converti questo prelievo in un deposito, l\'importo verrà :amount depositato in :sourceName anziché prelevato da esso.', 'convert_explanation_withdrawal_transfer' => 'Se converti questo prelievo in un trasferimento, l\'importo verrà :amount trasferito da :sourceName a un nuovo conto attività, invece di essere pagato a :destinationName.', 'convert_explanation_deposit_withdrawal' => 'Se converti questo deposito in un prelievo, l\'importo verrà rimosso :amount da :destinationName anziché aggiunto ad esso.', 'convert_explanation_deposit_transfer' => 'Se converti questo deposito in un trasferimento, :amount verrà trasferito da un conto attivo di tua scelta in :destinationName.', 'convert_explanation_transfer_withdrawal' => 'Se converti questo trasferimento in un prelievo, l\'importo :amount andrà da :sourceName a una nuova destinazione a titolo di spesa, anziché a :destinationName come trasferimento.', - 'convert_explanation_transfer_deposit' => 'Se converti questo trasferimento in un deposito, :amount verrà depositato nell\'account :destinationName anziché essere trasferito lì.', + 'convert_explanation_transfer_deposit' => 'Se converti questo trasferimento in un deposito, :amount verranno depositati nel conto :destinationName anziché esservi trasferiti.', 'converted_to_Withdrawal' => 'La transazione è stata convertita in un prelievo', 'converted_to_Deposit' => 'La transazione è stata convertita in un deposito', 'converted_to_Transfer' => 'La transazione è stata convertita in un trasferimento', - 'invalid_convert_selection' => 'Tl\'account che hai selezionato è già utilizzato in questa transazione o non esiste.', + 'invalid_convert_selection' => 'Il conto che hai selezionato è già utilizzato in questa transazione o non esiste.', 'source_or_dest_invalid' => 'Impossibile trovare i dettagli corretti della transazione. Non è possibile effettuare la conversione.', // create new stuff: @@ -594,7 +595,7 @@ return [ 'create_new_transfer' => 'Crea nuovo trasferimento', 'create_new_asset' => 'Crea un nuovo conto attività', 'create_new_expense' => 'Crea un nuovo conto di spesa', - 'create_new_revenue' => 'Crea un nuovo conto di entrate', + 'create_new_revenue' => 'Crea un nuovo conto entrate', 'create_new_piggy_bank' => 'Crea un nuovo salvadanaio', 'create_new_bill' => 'Crea una nuova bolletta', @@ -609,8 +610,8 @@ return [ 'updated_currency' => 'Valuta :name aggiornata', 'ask_site_owner' => 'Chiedi a :owner di aggiungere, rimuovere o modificare valute.', 'currencies_intro' => 'Firefly III supporta varie valute che è possibile impostare e abilitare qui.', - 'make_default_currency' => 'rendere predefinito', - 'default_currency' => 'predefinito', + 'make_default_currency' => 'rendi predefinita', + 'default_currency' => 'predefinita', // forms: 'mandatoryFields' => 'Campi obbligatori', @@ -631,11 +632,11 @@ return [ 'delete_budget' => 'Elimina budget ":name"', 'deleted_budget' => 'Budget eliminato ":name"', 'edit_budget' => 'Modifica budget ":name"', - 'updated_budget' => 'Budget aggiornato ":name"', - 'update_amount' => 'Importo aggiornato', + 'updated_budget' => 'Budget ":name" aggiornato', + 'update_amount' => 'Aggiorna importo', 'update_budget' => 'Budget aggiornato', - 'update_budget_amount_range' => 'Aggiornamento (previsto) importo disponibile tra :start and :end', - 'budget_period_navigator' => 'Navigatore periodico', + 'update_budget_amount_range' => 'Aggiorna l\'importo disponibile (previsto) tra il :start e il :end', + 'budget_period_navigator' => 'Navigatore dei periodi', 'info_on_available_amount' => 'Cosa ho a disposizione?', 'available_amount_indication' => 'Utilizza questi importi per ottenere un\'indicazione di quale potrebbe essere il tuo budget totale.', 'suggested' => 'Consigliato', @@ -645,11 +646,11 @@ return [ // bills: 'match_between_amounts' => 'La bolletta abbina le transazioni tra :low e :high.', 'bill_related_rules' => 'Regole relative a questa bolletta', - 'repeats' => 'Ripeti', + 'repeats' => 'Si ripete', 'connected_journals' => 'Transazioni connesse', 'auto_match_on' => 'Abbinato automaticamente da Firefly III', 'auto_match_off' => 'Non abbinato automaticamente a Firefly III', - 'next_expected_match' => 'Prossima partita prevista', + 'next_expected_match' => 'Prossimo abbinamento previsto', 'delete_bill' => 'Elimina bolletta ":name"', 'deleted_bill' => 'Bolletta eliminata ":name"', 'edit_bill' => 'Modifica bolletta ":name"', @@ -673,8 +674,8 @@ return [ // accounts: 'details_for_asset' => 'Dettagli per conto attività ":name"', 'details_for_expense' => 'Dettagli per conto spese ":name"', - 'details_for_revenue' => 'Dettagli per conto delle entrate ":name"', - 'details_for_cash' => 'Dettagli per conto in contanti ":name"', + 'details_for_revenue' => 'Dettagli per conto entrate ":name"', + 'details_for_cash' => 'Dettagli per il conto contanti ":name"', 'store_new_asset_account' => 'Salva nuovo conto attività', 'store_new_expense_account' => 'Salva il nuovo conto spese', 'store_new_revenue_account' => 'Salva il nuovo conto entrate', @@ -684,31 +685,31 @@ return [ 'delete_asset_account' => 'Elimina conto attività ":name"', 'delete_expense_account' => 'Elimina conto spese ":name"', 'delete_revenue_account' => 'Elimina conto entrate ":name"', - 'asset_deleted' => 'Conto attività eliminato correttamente ":name"', - 'expense_deleted' => 'Conto spese eliminato correttamente ":name"', - 'revenue_deleted' => 'Conto entrate eliminato correttamente ":name"', + 'asset_deleted' => 'Conto attività ":name" eliminato correttamente', + 'expense_deleted' => 'Conto spese ":name" eliminato correttamente', + 'revenue_deleted' => 'Conto entrate ":name" eliminato correttamente', 'update_asset_account' => 'Aggiorna conto attività', 'update_expense_account' => 'Aggiorna conto spese', 'update_revenue_account' => 'Aggiorna conto entrate', 'make_new_asset_account' => 'Crea un nuovo conto attività', 'make_new_expense_account' => 'Crea un nuovo conto spesa', 'make_new_revenue_account' => 'Crea nuovo conto entrate', - 'asset_accounts' => 'Conti patrimoniali', + 'asset_accounts' => 'Conti attività', 'expense_accounts' => 'Conti spese', 'revenue_accounts' => 'Conti entrate', - 'cash_accounts' => 'Conti cassa', - 'Cash account' => 'Conto cassa', + 'cash_accounts' => 'Conti contanti', + 'Cash account' => 'Conto contanti', 'reconcile_account' => 'Riconciliazione conto ":account"', 'delete_reconciliation' => 'Elimina riconciliazione', 'update_reconciliation' => 'Aggiorna riconciliazione', 'amount_cannot_be_zero' => 'L\'importo non può essere zero', 'end_of_reconcile_period' => 'Fine periodo riconciliazione: :period', 'start_of_reconcile_period' => 'Inizio periodo riconciliazione: :period', - 'start_balance' => 'Saldo inizio', - 'end_balance' => 'Saldo fine', + 'start_balance' => 'Saldo iniziale', + 'end_balance' => 'Saldo finale', 'update_balance_dates_instruction' => 'Abbina gli importi e le date sopra al tuo estratto conto e premi "Inizia la riconciliazione"', 'select_transactions_instruction' => 'Seleziona le transazioni che appaiono sul tuo estratto conto.', - 'select_range_and_balance' => 'Innanzitutto verifica l\'intervallo di date e i saldi. Quindi premere "Inizia riconciliazione"', + 'select_range_and_balance' => 'Innanzitutto verifica l\'intervallo di date e i saldi. Quindi premi "Inizia riconciliazione"', 'date_change_instruction' => 'Se cambi ora l\'intervallo di date, qualsiasi progresso andrà perso.', 'update_selection' => 'Aggiorna selezione', 'store_reconcile' => 'Memorizza la riconciliazione', @@ -718,9 +719,9 @@ return [ 'reconcile_options' => 'Opzioni di riconciliazione', 'reconcile_range' => 'Intervallo di riconciliazione', 'start_reconcile' => 'Avvia la riconciliazione', - 'cash' => 'cassa', + 'cash' => 'contanti', 'account_type' => 'Tipo conto', - 'save_transactions_by_moving' => 'Salva questa(e) transazione(i) spostandola(e) in un altro conto:', + 'save_transactions_by_moving' => 'Salva queste transazioni spostandole in un altro conto:', 'stored_new_account' => 'Nuovo conto ":name" stored!', 'updated_account' => 'Aggiorna conto ":name"', 'credit_card_options' => 'Opzioni Carta di Credito', @@ -734,19 +735,19 @@ return [ 'reconcile_has_more' => 'Il tuo conto Firefly III ha più denaro irispetto a quanto afferma la tua banca. Ci sono diverse opzioni Si prega di scegliere cosa fare. Quindi, premere "Conferma riconciliazione".', 'reconcile_has_less' => 'Il tuo conto Firefly III ha meno denaro rispetto a quanto afferma la tua banca. Ci sono diverse opzioni Si prega di scegliere cosa fare. Quindi, premi "Conferma riconciliazione".', 'reconcile_is_equal' => 'La tua contabilità di Firefly III e le tue coordinate bancarie corrispondono. Non c\'è niente da fare. Si prega di premere "Conferma riconciliazione" per confermare l\'inserimento.', - 'create_pos_reconcile_transaction' => 'Cancella le transazioni selezionate e crea una correzione aggiungendo :amount a questo conto attività.', - 'create_neg_reconcile_transaction' => 'Cancella le transazioni selezionate e crea una correzione rimuovendo :amount da questo conto attività.', - 'reconcile_do_nothing' => 'Cancella le transazioni selezionate, ma non correggere.', + 'create_pos_reconcile_transaction' => 'Concilia le transazioni selezionate e crea una correzione aggiungendo :amount a questo conto attività.', + 'create_neg_reconcile_transaction' => 'Concilia le transazioni selezionate e crea una correzione rimuovendo :amount da questo conto attività.', + 'reconcile_do_nothing' => 'Concilia le transazioni selezionate, ma non correggere.', 'reconcile_go_back' => 'Puoi sempre modificare o eliminare una correzione in un secondo momento.', 'must_be_asset_account' => 'È possibile riconciliare solo i conti attività', 'reconciliation_stored' => 'Riconciliazione memorizzata', 'reconcilliation_transaction_title' => 'Riconciliazione (:from a :to)', 'reconcile_this_account' => 'Riconcilia questo conto', 'confirm_reconciliation' => 'Conferma riconciliazione', - 'submitted_start_balance' => 'Bilancio di partenza presentato', + 'submitted_start_balance' => 'Saldo iniziale presentato', 'selected_transactions' => 'Transazioni selezionate (:count)', - 'already_cleared_transactions' => 'Transazioni selezionate (:count)', - 'submitted_end_balance' => 'Bilancio finale inviato', + 'already_cleared_transactions' => 'Transazioni già conciliate (:count)', + 'submitted_end_balance' => 'Saldo finale inviato', 'initial_balance_description' => 'Saldo iniziale per ":account"', // categories: @@ -766,45 +767,45 @@ return [ 'without_category_between' => 'Senza categoria tra :start e :end', // transactions: - 'update_withdrawal' => 'Aggiorna spesa', + 'update_withdrawal' => 'Aggiorna prelievo', 'update_deposit' => 'Aggiorna entrata', 'update_transfer' => 'Aggiorna trasferimento', - 'updated_withdrawal' => 'Spesa aggiornata ":description"', + 'updated_withdrawal' => 'Prelievo ":description" aggiornato', 'updated_deposit' => 'Entrata aggiornata ":description"', 'updated_transfer' => 'Trasferimento ":description" aggiornato', - 'delete_withdrawal' => 'Elimina spesa ":description"', + 'delete_withdrawal' => 'Elimina prelievo ":description"', 'delete_deposit' => 'Elimina entrata ":description"', 'delete_transfer' => 'Elimina trasferimento ":description"', - 'deleted_withdrawal' => 'Spesa eliminata correttamente ":description"', - 'deleted_deposit' => 'Entrata eliminata correttamente ":description"', + 'deleted_withdrawal' => 'Prelievo ":description" eliminato correttamente', + 'deleted_deposit' => 'Entrata ":description" eliminata correttamente', 'deleted_transfer' => 'Trasferimento ":description" eliminato correttamente', - 'stored_journal' => 'Nuova transazione creata correttamente ":description"', + 'stored_journal' => 'Nuova transazione ":description" creata correttamente', 'select_transactions' => 'Seleziona transazioni', 'rule_group_select_transactions' => 'Applica ":title" a transazioni', 'rule_select_transactions' => 'Applica ":title" a transazioni', 'stop_selection' => 'Smetti di selezionare le transazioni', 'reconcile_selected' => 'Riconcilia', - 'mass_delete_journals' => 'Elimina un numero di transazioni', - 'mass_edit_journals' => 'Modifica un numero di transazioni', - 'mass_bulk_journals' => 'Modifica in blocco un numero di transazioni', - 'mass_bulk_journals_explain' => 'Se non si desidera modificare le transazioni una alla volta utilizzando la funzione di modifica di massa, è possibile aggiornarle in una volta sola. Basta selezionare le categorie, le etichette o i budget preferiti nei campi sottostanti e tutte le transazioni nella tabella verranno aggiornate.', + 'mass_delete_journals' => 'Elimina un certo numero di transazioni', + 'mass_edit_journals' => 'Modifica un certo numero di transazioni', + 'mass_bulk_journals' => 'Modifica in blocco un certo numero di transazioni', + 'mass_bulk_journals_explain' => 'Se non si desidera modificare le transazioni una alla volta utilizzando la funzione di modifica in blocco, è possibile aggiornarle in una volta sola. Basta selezionare le categorie, le etichette o i budget preferiti nei campi sottostanti e tutte le transazioni nella tabella verranno aggiornate.', 'bulk_set_new_values' => 'Usa gli inserimenti qui sotto per impostare nuovi valori. Se li lasci vuoti, saranno resi vuoti per tutti. Inoltre, si noti che solo i prelievi avranno un budget.', 'no_bulk_category' => 'Non aggiornare la categoria', 'no_bulk_budget' => 'Non aggiornare il budget', - 'no_bulk_tags' => 'Non aggiornare la(e) etichetta(e)', - 'bulk_edit' => 'Modifica collettiva', - 'cannot_edit_other_fields' => 'Non puoi modificare in massa altri campi oltre a quelli qui, perché non c\'è spazio per mostrarli. Segui il link e modificali uno per uno, se è necessario modificare questi campi.', + 'no_bulk_tags' => 'Non aggiornare le etichette', + 'bulk_edit' => 'Modifica in blocco', + 'cannot_edit_other_fields' => 'Non puoi modificare in blocco altri campi oltre a quelli presenti perché non c\'è spazio per mostrarli. Segui il link e modificali uno per uno se è necessario modificare questi campi.', 'no_budget' => 'nessuno', - 'no_budget_squared' => '(nessun bilancio)', + 'no_budget_squared' => '(nessun budget)', 'perm-delete-many' => 'Eliminare molti oggetti in una volta sola può essere molto pericoloso. Per favore sii cauto.', - 'mass_deleted_transactions_success' => 'Transazione(i) eliminata(e) :amount.', - 'mass_edited_transactions_success' => 'Transazione(i) aggiornata(e) :amount.', + 'mass_deleted_transactions_success' => ':amount transazioni eliminate .', + 'mass_edited_transactions_success' => ':amount transazioni aggiornate', 'opt_group_no_account_type' => '(nessun tipo di conto)', 'opt_group_defaultAsset' => 'Conto attività predefinito', 'opt_group_savingAsset' => 'Conti risparmio', - 'opt_group_sharedAsset' => 'Conti risorse condivise', + 'opt_group_sharedAsset' => 'Conti attività condivisi', 'opt_group_ccAsset' => 'Carte di credito', - 'opt_group_cashWalletAsset' => 'Contanti', + 'opt_group_cashWalletAsset' => 'Portafogli', 'notes' => 'Note', 'unknown_journal_error' => 'Impossibile memorizzare la transazione. Controllare i file di log.', @@ -812,19 +813,19 @@ return [ 'welcome' => 'Benvenuto in Firefly III!', 'submit' => 'Invia', 'getting_started' => 'Inizia', - 'to_get_started' => 'È bello vedere che hai installato Firefly III con successo. Per iniziare con questo strumento, inserisci il nome della tua banca e il saldo del tuo conto corrente principale. Non preoccuparti se hai più account. È possibile aggiungere quelli più tardi. Firefly III ha bisogno di qualcosa per iniziare.', - 'savings_balance_text' => 'Firefly III creerà automaticamente un conto di risparmio per te. Per impostazione predefinita, non ci saranno soldi nel tuo conto di risparmio, ma se comunichi a Firefly III il saldo verrà archiviato come tale.', + 'to_get_started' => 'È bello vedere che hai installato Firefly III con successo. Per iniziare con questo strumento, inserisci il nome della tua banca e il saldo del tuo conto corrente principale. Non preoccuparti se hai più conti. È possibile aggiungere quelli più tardi. Firefly III ha bisogno di qualcosa per iniziare.', + 'savings_balance_text' => 'Firefly III creerà automaticamente un conto di risparmio per te. Per impostazione predefinita, non ci saranno soldi nel tuo conto di risparmio, ma se comunichi a Firefly III il saldo verrà salvato come tale.', 'finish_up_new_user' => 'Questo è tutto! Puoi continuare premendo Invia. Verrai indirizzato all\'indice di Firefly III.', 'stored_new_accounts_new_user' => 'I tuoi nuovi conti sono stati salvati.', 'set_preferred_language' => 'Se preferisci usare Firefly III in un\'altra lingua, impostala qui.', 'language' => 'Lingua', 'new_savings_account' => 'Conto di risparmio :bank_name', - 'cash_wallet' => 'Contanti', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'cash_wallet' => 'Portafoglio', + 'currency_not_present' => 'Se la valuta che usi normalmente non è elencata, non preoccuparti. Puoi creare le tue valute in Opzioni > Valute.', // home page: 'yourAccounts' => 'I tuoi conti', - 'budgetsAndSpending' => 'Bilanci e Spese', + 'budgetsAndSpending' => 'Budget e spese', 'savings' => 'Risparmi', 'newWithdrawal' => 'Nuova uscita', 'newDeposit' => 'Nuovo deposito', @@ -845,26 +846,26 @@ return [ 'currencies' => 'Valute', 'accounts' => 'Conti', 'Asset account' => 'Conto attività', - 'Default account' => 'Conto predefinito', + 'Default account' => 'Conto attività', 'Expense account' => 'Conto spese', 'Revenue account' => 'Conto entrate', 'Initial balance account' => 'Saldo iniziale conto', - 'budgets' => 'Bilanci', + 'budgets' => 'Budget', 'tags' => 'Etichette', 'reports' => 'Resoconti', 'transactions' => 'Transazioni', 'expenses' => 'Spese', - 'income' => 'Reddito / Entrata', + 'income' => 'Redditi / entrate', 'transfers' => 'Trasferimenti', - 'moneyManagement' => 'Gestione danaro', + 'moneyManagement' => 'Gestione denaro', 'piggyBanks' => 'Salvadanai', 'bills' => 'Bollette', - 'withdrawal' => 'Uscite', + 'withdrawal' => 'Prelievo', 'opening_balance' => 'Saldo di apertura', 'deposit' => 'Entrata', 'account' => 'Conto', 'transfer' => 'Trasferimento', - 'Withdrawal' => 'Spesa', + 'Withdrawal' => 'Prelievo', 'Deposit' => 'Entrata', 'Transfer' => 'Trasferimento', 'bill' => 'Bolletta', @@ -896,18 +897,17 @@ return [ 'reports_can_bookmark' => 'Ricorda che i resoconti possono essere aggiunti ai segnalibri.', 'incomeVsExpenses' => 'Entrate verso spese', 'accountBalances' => 'Saldo dei conti', - 'balanceStart' => 'Saldo inizio periodo', - 'balanceEnd' => 'Saldo fine del periodo', + 'balanceStart' => 'Saldo all\'inizio del periodo', + 'balanceEnd' => 'Saldo alla fine del periodo', 'splitByAccount' => 'Dividi per conto', 'coveredWithTags' => 'Coperto con etichetta', - 'leftUnbalanced' => 'Sbilanciato a sinistra', - 'leftInBudget' => 'Rimasto nel budget', + 'leftInBudget' => 'Rimanenza nel budget', 'sumOfSums' => 'Somma dei conti', 'noCategory' => '(nessuna categoria)', - 'notCharged' => 'Non caricato (ancora)', + 'notCharged' => 'Non (ancora) addebitato', 'inactive' => 'Disattivo', 'active' => 'Attivo', - 'difference' => 'Differenze', + 'difference' => 'Differenza', 'money_flowing_in' => 'Entrate', 'money_flowing_out' => 'Uscite', 'topX' => 'Superiore :number', @@ -915,13 +915,13 @@ return [ 'show_only_top' => 'Mostra solo in alto :number', 'report_type' => 'Tipo resoconto', 'report_type_default' => 'Resoconto finanziario predefinito', - 'report_type_audit' => 'Panoramica cronologica delle transazioni (controllo)', + 'report_type_audit' => 'Panoramica cronologica delle transazioni (verifica)', 'report_type_category' => 'Resoconto categoria', 'report_type_budget' => 'Resoconto budget', 'report_type_tag' => 'Resoconto etichetta', 'report_type_account' => 'Resoconto conto spese/entrate', 'more_info_help' => 'Ulteriori informazioni su questi tipi di resoconti sono disponibili nelle pagine della guida. Premi l\'icona (?) nell\'angolo in alto a destra.', - 'report_included_accounts' => 'Conti inclusi', + 'report_included_accounts' => 'Inclusi i conti', 'report_date_range' => 'Date intervallo', 'report_preset_ranges' => 'Intervalli preimpostati', 'shared' => 'Condiviso', @@ -930,37 +930,37 @@ return [ 'expense_entry' => 'Spese per il conto ":name" tra :start e :end', 'category_entry' => 'Spese nella categoria ":name" tra :start e :end', 'budget_spent_amount' => 'Spese nel budget ":budget" tra :start e :end', - 'balance_amount' => 'Spese nel budget ":budget" pagate dal conto ":account" tra :start e :end', - 'no_audit_activity' => 'Nessuna attività è stata registrata nel conto :account_name tra :start e :end.', - 'audit_end_balance' => 'Il saldo del conto di :account_name alla fine di :end era: :balance', + 'balance_amount' => 'Spese nel budget ":budget" pagate dal conto ":account" tra il :start e il :end', + 'no_audit_activity' => 'Nessuna attività è stata registrata nel conto :account_name tra il :start e il :end.', + 'audit_end_balance' => 'Il saldo del conto :account_name alla fine di :end era: :balance', 'reports_extra_options' => 'Opzioni extra', 'report_has_no_extra_options' => 'Questo resoconto non ha opzioni extra', 'reports_submit' => 'Mostra resoconto', 'end_after_start_date' => 'La data della fine del resoconto deve essere successiva alla data di inizio.', - 'select_category' => 'Seleziona la(e) categoria (e)', + 'select_category' => 'Seleziona le categorie', 'select_budget' => 'Seleziona i budget.', - 'select_tag' => 'Seleziona etichetta(e).', - 'income_per_category' => 'Reddito per categoria', - 'expense_per_category' => 'Spese per categoria', + 'select_tag' => 'Seleziona etichette.', + 'income_per_category' => 'Entrate per categoria', + 'expense_per_category' => 'Spesa per categoria', 'expense_per_budget' => 'Spese per budget', - 'income_per_account' => 'Reddito per conto', + 'income_per_account' => 'Entrate per conto', 'expense_per_account' => 'Spese per conto', 'expense_per_tag' => 'Spese per etichetta', - 'income_per_tag' => 'Reddito per etichetta', - 'include_expense_not_in_budget' => 'Spese non incluse nel(nei) bilancio(i) selezionato(i)', - 'include_expense_not_in_account' => 'Spese non incluse nel(nei) conto(i) selezionato(i)', - 'include_expense_not_in_category' => 'Spese incluse / non incluse nella(e) categoria(e) selezionata(e)', - 'include_income_not_in_category' => 'Entrate non incluse nella(e) categoria(e) selezionata(e)', - 'include_income_not_in_account' => 'Entrate non incluse nel(i) conto(i) selezionato(i)', - 'include_income_not_in_tags' => 'Entrate incluse / non incluse nella(e) etichetta(e) selezionata(e)', - 'include_expense_not_in_tags' => 'Spese incluse / non incluse nella(e) etichetta(e) selezionata(e)', + 'income_per_tag' => 'Entrate per etichetta', + 'include_expense_not_in_budget' => 'Incluse le spese non appartenenti ai budget selezionati', + 'include_expense_not_in_account' => 'Incluse le spese non appartenenti ai conti selezionati', + 'include_expense_not_in_category' => 'Incluse le spese non appartenenti alle categorie selezionate', + 'include_income_not_in_category' => 'Incluse le entrate non appartenenti alle categorie selezionate', + 'include_income_not_in_account' => 'Incluse le entrate non appartenenti ai conti selezionati', + 'include_income_not_in_tags' => 'Incluse le entrate non appartenenti alle etichette selezionate', + 'include_expense_not_in_tags' => 'Incluse le spese non appartenenti alle etichette selezionate', 'everything_else' => 'Tutto il resto', 'income_and_expenses' => 'Entrate e spese', - 'spent_average' => 'Speso (media)', - 'income_average' => 'Reddito (media)', + 'spent_average' => 'Spesi (media)', + 'income_average' => 'Entrate (media)', 'transaction_count' => 'Conteggio transazioni', 'average_spending_per_account' => 'Spesa media per conto', - 'average_income_per_account' => 'Reddito medio per conto', + 'average_income_per_account' => 'Media entrate per conto', 'total' => 'Totale', 'description' => 'Descrizione', 'sum_of_period' => 'Somma del periodo', @@ -975,7 +975,7 @@ return [ 'in_out_accounts' => 'Guadagnati e spesi per combinazione', 'in_out_per_category' => 'Guadagnati e spesi per categoria', 'out_per_budget' => 'Speso per budget', - 'select_expense_revenue' => 'Seleziona conto spese / entrate', + 'select_expense_revenue' => 'Seleziona conto spese/entrate', // charts: 'chart' => 'Grafico', @@ -992,7 +992,7 @@ return [ 'journal-amount' => 'Entrata fattura corrente', 'name' => 'Nome', 'date' => 'Data', - 'paid' => 'Pagato', + 'paid' => 'Pagati', 'unpaid' => 'Da pagare', 'day' => 'Giorno', 'budgeted' => 'Preventivato', @@ -1018,7 +1018,7 @@ return [ 'remove_money_from_piggy_title' => 'Rimuovi i soldi dal salvadanaio ":name"', 'add' => 'Aggiungi', 'no_money_for_piggy' => 'non hai soldi da mettere in questo salvadanaio.', - 'suggested_savings_per_month' => 'Suggested per month', + 'suggested_savings_per_month' => 'Suggeriti per mese', 'remove' => 'Rimuovi', 'max_amount_add' => 'L\'importo massimo che puoi aggiungere è', @@ -1043,16 +1043,16 @@ return [ // tags 'delete_tag' => 'Elimina etichetta ":tag"', - 'deleted_tag' => 'Etichetta eliminata ":tag"', + 'deleted_tag' => 'Etichetta ":tag" eliminata', 'new_tag' => 'Crea nuova etichetta', 'edit_tag' => 'Modifica etichetta ":tag"', - 'updated_tag' => 'Etichetta aggiornata ":tag"', + 'updated_tag' => 'Etichetta ":tag" aggiornata', 'created_tag' => 'Etichetta ":tag" creata correttamente', 'transaction_journal_information' => 'Informazione Transazione', 'transaction_journal_meta' => 'Meta informazioni', 'total_amount' => 'Importo totale', - 'number_of_decimals' => 'Numero decimali', + 'number_of_decimals' => 'Cifre decimali', // administration 'administration' => 'Amministrazione', @@ -1060,7 +1060,7 @@ return [ 'list_all_users' => 'Tutti gli utenti', 'all_users' => 'Tutti gli utenti', 'instance_configuration' => 'Configurazione', - 'firefly_instance_configuration' => 'Opzioni Configurazione di Firefly III', + 'firefly_instance_configuration' => 'Opzioni di configurazione di Firefly III', 'setting_single_user_mode' => 'Modo utente singolo', 'setting_single_user_mode_explain' => 'Per impostazione predefinita, Firefly III accetta solo una (1) registrazione: tu. Questa è una misura di sicurezza, che impedisce ad altri di usare la tua istanza a meno che tu non le autorizzi. Le future registrazioni sono bloccate. Bene! quando deselezioni questa casella, gli altri possono usare la tua istanza, supponendo che possano raggiungerla (quando è connessa a Internet).', 'store_configuration' => 'Salva configurazione', @@ -1072,19 +1072,19 @@ return [ 'total_size' => 'dimensione totale', 'budget_or_budgets' => 'budget', 'budgets_with_limits' => 'budget con importo configurato', - 'nr_of_rules_in_total_groups' => 'regola(e) :count_rules in gruppo(i) :count_groups', - 'tag_or_tags' => 'etichetta(e)', + 'nr_of_rules_in_total_groups' => ':count_rules regole in :count_groups gruppi di regole', + 'tag_or_tags' => 'etichette', 'configuration_updated' => 'La configurazione è stata aggiornata', 'setting_is_demo_site' => 'Sito Demo', 'setting_is_demo_site_explain' => 'Se si seleziona questa casella, questa installazione si comporterà come se fosse il sito demo, che può avere strani effetti collaterali.', - 'block_code_bounced' => 'Il(i) messaggio(i) email rimbalzati', + 'block_code_bounced' => 'Messaggi email respinti', 'block_code_expired' => 'Conto demo scaduto', 'no_block_code' => 'Nessun motivo per bloccare o non bloccare un utente', 'block_code_email_changed' => 'L\'utente non ha ancora confermato il nuovo indirizzo emails', 'admin_update_email' => 'Contrariamente alla pagina del profilo, l\'utente NON riceverà alcuna notifica al proprio indirizzo email!', 'update_user' => 'Aggiorna utente', 'updated_user' => 'I dati dell\'utente sono stati modificati.', - 'delete_user' => 'Elimia utente :email', + 'delete_user' => 'Elimina utente :email', 'user_deleted' => 'L\'utente è stato eliminato', 'send_test_email' => 'Invia un messaggio di posta elettronica di prova', 'send_test_email_text' => 'Per vedere se la tua installazione è in grado di inviare e-mail, ti preghiamo di premere questo pulsante. Qui non vedrai un errore (se presente), i file di log rifletteranno eventuali errori. Puoi premere questo pulsante tutte le volte che vuoi. Non c\'è controllo dello spam. Il messaggio verrà inviato a :email e dovrebbe arrivare a breve.', @@ -1092,12 +1092,12 @@ return [ 'send_test_triggered' => 'Il test è stato attivato. Controlla la tua casella di posta e i file di log.', // links - 'journal_link_configuration' => 'Configurazione dei collegamenti di transazione', + 'journal_link_configuration' => 'Configurazione dei collegamenti di una transazione', 'create_new_link_type' => 'Crea un nuovo tipo di collegamento', 'store_new_link_type' => 'Memorizza nuovo tipo di collegamento', 'update_link_type' => 'Aggiorna il tipo di collegamento', 'edit_link_type' => 'Modifica il tipo di collegamento ":name"', - 'updated_link_type' => 'Tipo di collegamento aggiornato ":name"', + 'updated_link_type' => 'Tipo del collegamento ":name" aggiornato', 'delete_link_type' => 'Elimina il tipo di collegamento ":name"', 'deleted_link_type' => 'Tipo di collegamento eliminato ":name"', 'stored_new_link_type' => 'Memorizza nuovo tipo di collegamento ":name"', @@ -1105,7 +1105,7 @@ return [ 'link_type_help_name' => 'Ie. "Duplicati"', 'link_type_help_inward' => 'Ie. "duplicati"', 'link_type_help_outward' => 'Ie. "è duplicato da"', - 'save_connections_by_moving' => 'Salvare il collegamento tra questa(e) transazione(i) spostandola(e) su un altro tipo di collegamento:', + 'save_connections_by_moving' => 'Salva il collegamento tra queste transazioni spostandole su un altro tipo di collegamento:', 'do_not_save_connection' => '(non salvare la connessione)', 'link_transaction' => 'Collega transazione', 'link_to_other_transaction' => 'Collega questa transazione a un\'altra transazione', @@ -1119,7 +1119,7 @@ return [ 'journals_error_linked' => 'Queste transazioni sono già collegate.', 'journals_link_to_self' => 'Non puoi collegare una transazione con se stessa', 'journal_links' => 'Collegamenti di transazione', - 'this_withdrawal' => 'Questa spesa', + 'this_withdrawal' => 'Questo prelievo', 'this_deposit' => 'Questa entrata', 'this_transfer' => 'Questo trasferimento', 'overview_for_link' => 'Panoramica per tipo di collegamento ":name"', @@ -1131,11 +1131,13 @@ return [ // link translations: 'relates to_inward' => 'inerente a', - 'is (partially) refunded by_inward' => 'è (parzialmente) rimborsato da', - 'is (partially) paid for by_inward' => 'è (parzialmente) pagato da', - 'is (partially) reimbursed by_inward' => 'è (parzialmente) rimborsato da', + 'is (partially) refunded by_inward' => 'è (parzialmente) rimborsata da', + 'is (partially) paid for by_inward' => 'è (parzialmente) pagata da', + 'is (partially) reimbursed by_inward' => 'è (parzialmente) rimborsata da', + 'inward_transaction' => 'Transazione in ingresso', + 'outward_transaction' => 'Transazione in uscita', 'relates to_outward' => 'inerente a', - '(partially) refunds_outward' => '(parzialmente) rimborsi', + '(partially) refunds_outward' => '(parzialmente) rimborsa', '(partially) pays for_outward' => '(parzialmente) paga per', '(partially) reimburses_outward' => '(parzialmente) rimborsa', @@ -1144,35 +1146,36 @@ return [ 'add_another_split' => 'Aggiungi un\'altra divisione', 'split-transactions' => 'Dividi le transazioni', 'do_split' => 'Fai una divisione', - 'split_this_withdrawal' => 'Dividi questa spesa', + 'split_this_withdrawal' => 'Dividi questo prelievo', 'split_this_deposit' => 'Dividi questa entrata', 'split_this_transfer' => 'Dividi questo trasferimento', 'cannot_edit_multiple_source' => 'Non è possibile modificare la transazione n. #:id con la descrizione ":description" perché contiene più conti di origine.', 'cannot_edit_multiple_dest' => 'Non è possibile modificare la transazione n. #:id con la descrizione ":description" perché contiene più conti di destinazione.', 'cannot_edit_reconciled' => 'Non è possibile modificare la transazione n. #:id con la descrizione ":description" perché è stata contrassegnata come riconciliata.', - 'cannot_edit_opening_balance' => 'Non è possibile modificare il bilancio di apertura di un conto.', + 'cannot_edit_opening_balance' => 'Non è possibile modificare il saldo di apertura di un conto.', 'no_edit_multiple_left' => 'Non hai selezionato transazioni valide da modificare.', 'cannot_convert_split_journal' => 'Impossibile convertire una transazione divisa', // Import page (general strings only) - 'import_index_title' => 'Importa i dati in Firefly III', - 'import_data' => 'Importa i dati', + 'import_index_title' => 'Importa le transazioni in Firefly III', + 'import_data' => 'Importa dati', + 'import_transactions' => 'Importa transazioni', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Questa funzione non è disponibile quando si utilizza Firefly III in un ambiente Sandstorm.io.', // empty lists? no objects? instructions: 'no_accounts_title_asset' => 'Creiamo un conto attività!', - 'no_accounts_intro_asset' => 'Non hai ancora un conto attività. I conti cespiti sono i tuoi conti principali: il tuo conto corrente, il tuo conto di risparmio, il conto condiviso o persino la tua carta di credito.', - 'no_accounts_imperative_asset' => 'Per iniziare a utilizzare Firefly III è necessario creare almeno un conto attività. Facciamo così ora:', + 'no_accounts_intro_asset' => 'Non hai ancora un conto attività. I conti attività sono i tuoi conti principali: il tuo conto corrente, il tuo conto di risparmio, il conto condiviso o persino la tua carta di credito.', + 'no_accounts_imperative_asset' => 'Per iniziare a utilizzare Firefly III è necessario creare almeno un conto attività. Facciamolo ora:', 'no_accounts_create_asset' => 'Crea un conto attività', 'no_accounts_title_expense' => 'Creiamo un conto spese!', 'no_accounts_intro_expense' => 'Non hai ancora un conto spesa. I conti spese sono i luoghi in cui si spendono soldi, come negozi e supermercati.', 'no_accounts_imperative_expense' => 'I conti spesa vengono creati automaticamente quando si creano le transazioni, ma è possibile crearne anche manualmente, se lo si desidera. Ne creiamo uno ora:', 'no_accounts_create_expense' => 'Crea conto spesa', 'no_accounts_title_revenue' => 'Creiamo un conto entrate!', - 'no_accounts_intro_revenue' => 'Non hai ancora un conto entrate. I conti delle entrate sono i luoghi in cui ricevi denaro, come il tuo stipendio e altre entrate.', - 'no_accounts_imperative_revenue' => 'I conti entrate vengono creati automaticamente quando si creano le transazioni, ma è possibile crearne anche manualmente, se lo si desidera. Ne creiamo uno ora:', + 'no_accounts_intro_revenue' => 'Non hai ancora un conto entrate. I conti delle entrate sono i luoghi da cui ricevi denaro, come il tuo datore di lavoro.', + 'no_accounts_imperative_revenue' => 'I conti entrate vengono creati automaticamente quando si creano le transazioni, ma è possibile crearne anche manualmente, se lo si desidera. Creiamone uno ora:', 'no_accounts_create_revenue' => 'Crea conto entrate', 'no_budgets_title_default' => 'Creiamo un budget', 'no_budgets_intro_default' => 'Non hai ancora budget. I budget sono usati per organizzare le tue spese in gruppi logici, ai quali puoi dare un tetto indicativo per limitare le tue spese.', @@ -1191,7 +1194,7 @@ return [ 'no_transactions_imperative_withdrawal' => 'Hai effettuato delle spese? Dovresti registrarle:', 'no_transactions_create_withdrawal' => 'Crea spese', 'no_transactions_title_deposit' => 'Creiamo delle entrate!', - 'no_transactions_intro_deposit' => 'non hai ancora entrate registrate. È necessario creare voci di reddito per iniziare a gestire le tue finanze.', + 'no_transactions_intro_deposit' => 'Non hai ancora delle entrate registrate. Dovresti creare delle voci di entrata per iniziare a gestire le tue finanze.', 'no_transactions_imperative_deposit' => 'Hai ricevuto dei soldi? Dovresti scriverlo:', 'no_transactions_create_deposit' => 'Crea una entrata', 'no_transactions_title_transfers' => 'Creiamo un trasferimento!', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Non hai ancora nessuna bolletta. Puoi creare bollette per tenere traccia delle spese regolari, come il tuo affitto o l\'assicurazione.', 'no_bills_imperative_default' => 'Hai delle bollette regolari? Crea una bolletta e tieni traccia dei tuoi pagamenti:', 'no_bills_create_default' => 'Crea bolletta', + + // recurring transactions + 'recurrences' => 'Transazioni ricorrenti', + 'no_recurring_title_default' => 'Creiamo una transazione ricorrente!', + 'no_recurring_intro_default' => 'Non hai ancora una transazione ricorrente. Puoi utilizzare queste per lasciare che Firefly III crei automaticamente le transazioni per te.', + 'no_recurring_imperative_default' => 'Questa è una caratteristica piuttosto avanzata che può essere estremamente utile. Assicurati di leggere la documentazione (l\'icona (?) nell\'angolo in alto a destra) prima di continuare.', + 'no_recurring_create_default' => 'Crea una transazione ricorrente', + 'make_new_recurring' => 'Crea una transazione ricorrente', + 'recurring_daily' => 'Ogni giorno', + 'recurring_weekly' => 'Ogni settimana di :weekday', + 'recurring_monthly' => 'Ogni mese il giorno :dayOfMonth', + 'recurring_ndom' => 'Ogni :dayOfMonth° :weekday del mese', + 'recurring_yearly' => 'Ogni anno il :date', + 'overview_for_recurrence' => 'Panoramica per la transazione ricorrente ":title"', + 'warning_duplicates_repetitions' => 'In alcuni casi rari è possibile che delle date appaiano due volte in questa lista. Questo può succedere quando ripetizioni multiple collidono. Firefly III si assicurerà sempre di generare una sola transazione per giorno.', + 'created_transactions' => 'Transazioni collegate', + 'expected_Withdrawals' => 'Prelievi previsti', + 'expected_Deposits' => 'Deposito previsto', + 'expected_Transfers' => 'Trasferimento previsto', + 'created_Withdrawals' => 'Prelievi creati', + 'created_Deposits' => 'Depositi creati', + 'created_Transfers' => 'Trasferimenti creati', + 'created_from_recurrence' => 'Creata dalla transazione ricorrente ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Etichette', + 'recurring_meta_field_notes' => 'Note', + 'recurring_meta_field_bill_id' => 'Bolletta', + 'recurring_meta_field_piggy_bank_id' => 'Salvadanaio', + 'create_new_recurrence' => 'Crea una nuova transazione ricorrente', + 'help_first_date' => 'Indica quando la ricorrenza dovrebbe avvenire per la prima volta. Questo deve essere nel futuro.', + 'help_first_date_no_past' => 'Indica quando la ricorrenza dovrebbe avvenire per la prima volta. Firefly III non creerà transazioni nel passato.', + 'no_currency' => '(nessuna valuta)', + 'mandatory_for_recurring' => 'Informazioni obbligatorie sulla ricorrenza', + 'mandatory_for_transaction' => 'Informazioni obbligatorie sulla transazione', + 'optional_for_recurring' => 'Informazioni facoltative sulla ricorrenza', + 'optional_for_transaction' => 'Informazioni facoltative sulla transazione', + 'change_date_other_options' => 'Cambia la "prima volta" per visualizzare maggiori opzioni.', + 'mandatory_fields_for_tranaction' => 'Il valore qui presente finirà nella transazione che verrà creata', + 'click_for_calendar' => 'Clicca qui per visualizzare in un calendario quando la transazione si ripete.', + 'repeat_forever' => 'Ripeti per sempre', + 'repeat_until_date' => 'Ripeti fino alla data', + 'repeat_times' => 'Ripeti per un certo numero di volte', + 'recurring_skips_one' => 'Una volta sì e una volta no', + 'recurring_skips_more' => 'Salta per :count volte', + 'store_new_recurrence' => 'Salva transazione ricorrente', + 'stored_new_recurrence' => 'La transazione ricorrente ":title" è stata salvata con successo.', + 'edit_recurrence' => 'Modifica transazione ricorrente ":title"', + 'recurring_repeats_until' => 'Si ripete fino al :date', + 'recurring_repeats_forever' => 'Si ripete per sempre', + 'recurring_repeats_x_times' => 'Si ripete per :count volte', + 'update_recurrence' => 'Aggiorna transazione ricorrente', + 'updated_recurrence' => 'Transazione ricorrente ":title" aggiornata', + 'recurrence_is_inactive' => 'Questa transazione ricorrente non è attiva e non genererà nuove transazioni.', + 'delete_recurring' => 'Elimina transazione ricorrente ":title"', + 'new_recurring_transaction' => 'Nuova transazione ricorrente', + 'help_weekend' => 'Cosa vuoi che Firefly III faccia quando la transazione ricorrente cade di sabato o domenica?', + 'do_nothing' => 'Crea la transazione', + 'skip_transaction' => 'Salta l\'occorrenza', + 'jump_to_friday' => 'Crea la transazione il venerdì precedente', + 'jump_to_monday' => 'Crea la transazione il lunedì successivo', + 'will_jump_friday' => 'Verrà creata il venerdì anziché nel fine settimana.', + 'will_jump_monday' => 'Verrà creata il lunedì anziché il fine settimana.', + 'except_weekends' => 'Tranne il fine settimana', + 'recurrence_deleted' => 'La transazione ricorrente ":title" è stata eliminata', ]; diff --git a/resources/lang/it_IT/form.php b/resources/lang/it_IT/form.php index 2c47c1601f..41360638d9 100644 --- a/resources/lang/it_IT/form.php +++ b/resources/lang/it_IT/form.php @@ -24,162 +24,165 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Nome banca', - 'bank_balance' => 'Saldo', - 'savings_balance' => 'Saldo risparmio', - 'credit_card_limit' => 'Limite Carta di Credito', - 'automatch' => 'Partita automaticamente', - 'skip' => 'Salta', - 'name' => 'Nome', - 'active' => 'Attivo', - 'amount_min' => 'Importo minimo', - 'amount_max' => 'Importo massimo', - 'match' => 'Partite su', - 'strict' => 'Modalità severa', - 'repeat_freq' => 'Ripetizioni', - 'journal_currency_id' => 'Valuta', - 'currency_id' => 'Valuta', - 'transaction_currency_id' => 'Valuta', - 'external_ip' => 'L\'IP esterno del tuo server', - 'attachments' => 'Allegati', - 'journal_amount' => 'Importo', - 'journal_source_account_name' => 'Conto entrate (origine)', - 'journal_source_account_id' => 'Conto attività (origine)', - 'BIC' => 'BIC', - 'verify_password' => 'Verifica password di sicurezza', - 'source_account' => 'Conto sorgente', - 'destination_account' => 'Conto destinazione', - 'journal_destination_account_id' => 'Conto attività (destinatione)', - 'asset_destination_account' => 'Conto attività (destinatione)', - 'asset_source_account' => 'Conto attività (sorgente)', - 'journal_description' => 'Descrizione', - 'note' => 'Note', - 'split_journal' => 'Dividi questa transazione', - 'split_journal_explanation' => 'Dividi questa transazione in più parti', - 'currency' => 'Valuta', - 'account_id' => 'Conto attività', - 'budget_id' => 'Budget', - 'openingBalance' => 'Saldo di apertura', - 'tagMode' => 'Modalità etichetta', - 'tag_position' => 'Posizione etichetta', - 'virtualBalance' => 'Saldo virtuale', - 'targetamount' => 'Importo obiettivo', - 'accountRole' => 'Ruolo del conto', - 'openingBalanceDate' => 'Data di apertura del bilancio', - 'ccType' => 'Piano di pagamento con carta di credito', - 'ccMonthlyPaymentDate' => 'Data di pagamento mensile della carta di credito', - 'piggy_bank_id' => 'Salvadanaio', - 'returnHere' => 'Ritorna qui', - 'returnHereExplanation' => 'Dopo averlo archiviato, torna qui per crearne un altro.', - 'returnHereUpdateExplanation' => 'Dopo l\'aggiornamento, torna qui.', - 'description' => 'Descrizione', - 'expense_account' => 'Conto spese', - 'revenue_account' => 'Conto entrate', - 'decimal_places' => 'Decimali', - 'exchange_rate_instruction' => 'Valuta straniera', - 'source_amount' => 'Importo (origine)', - 'destination_amount' => 'Importo (destinazione)', - 'native_amount' => 'Importo nativo', - 'new_email_address' => 'Nuovo indirizzo email', - 'verification' => 'Verifica', - 'api_key' => 'Chiave API', - 'remember_me' => 'Ricordami', + 'bank_name' => 'Nome banca', + 'bank_balance' => 'Saldo', + 'savings_balance' => 'Saldo risparmio', + 'credit_card_limit' => 'Limite Carta di Credito', + 'automatch' => 'Abbina automaticamente', + 'skip' => 'Salta ogni', + 'name' => 'Nome', + 'active' => 'Attivo', + 'amount_min' => 'Importo minimo', + 'amount_max' => 'Importo massimo', + 'match' => 'Abbina con', + 'strict' => 'Modalità severa', + 'repeat_freq' => 'Si ripete', + 'journal_currency_id' => 'Valuta', + 'currency_id' => 'Valuta', + 'transaction_currency_id' => 'Valuta', + 'external_ip' => 'L\'IP esterno del tuo server', + 'attachments' => 'Allegati', + 'journal_amount' => 'Importo', + 'journal_source_name' => 'Conto entrate (origine)', + 'journal_source_id' => 'Conto attività (origine)', + 'BIC' => 'BIC', + 'verify_password' => 'Verifica password di sicurezza', + 'source_account' => 'Conto sorgente', + 'destination_account' => 'Conto destinazione', + 'journal_destination_id' => 'Conto attività (destinazione)', + 'asset_destination_account' => 'Conto attività (destinazione)', + 'asset_source_account' => 'Conto attività (sorgente)', + 'journal_description' => 'Descrizione', + 'note' => 'Note', + 'split_journal' => 'Dividi questa transazione', + 'split_journal_explanation' => 'Dividi questa transazione in più parti', + 'currency' => 'Valuta', + 'account_id' => 'Conto attività', + 'budget_id' => 'Budget', + 'openingBalance' => 'Saldo di apertura', + 'tagMode' => 'Modalità etichetta', + 'tag_position' => 'Posizione etichetta', + 'virtualBalance' => 'Saldo virtuale', + 'targetamount' => 'Importo obiettivo', + 'accountRole' => 'Ruolo del conto', + 'openingBalanceDate' => 'Data saldo di apertura', + 'ccType' => 'Piano di pagamento con carta di credito', + 'ccMonthlyPaymentDate' => 'Data di addebito mensile della carta di credito', + 'piggy_bank_id' => 'Salvadanaio', + 'returnHere' => 'Ritorna qui', + 'returnHereExplanation' => 'Dopo averlo archiviato, torna qui per crearne un altro.', + 'returnHereUpdateExplanation' => 'Dopo l\'aggiornamento, torna qui.', + 'description' => 'Descrizione', + 'expense_account' => 'Conto spese', + 'revenue_account' => 'Conto entrate', + 'decimal_places' => 'Decimali', + 'exchange_rate_instruction' => 'Valuta straniera', + 'source_amount' => 'Importo (origine)', + 'destination_amount' => 'Importo (destinazione)', + 'native_amount' => 'Importo nativo', + 'new_email_address' => 'Nuovo indirizzo email', + 'verification' => 'Verifica', + 'api_key' => 'Chiave API', + 'remember_me' => 'Ricordami', - 'source_account_asset' => 'Conto origine (conto risorse)', + 'source_account_asset' => 'Conto origine (conto attività)', 'destination_account_expense' => 'Conto destinazione (conto spese)', - 'destination_account_asset' => 'Conto destinazione (conto risorse)', + 'destination_account_asset' => 'Conto destinazione (conto attività)', 'source_account_revenue' => 'Conto sorgente (conto entrate)', 'type' => 'Tipo', - 'convert_Withdrawal' => 'Converti spesa', + 'convert_Withdrawal' => 'Converti prelievo', 'convert_Deposit' => 'Converti entrata', 'convert_Transfer' => 'Converti trasferimento', - 'amount' => 'Importo', - 'foreign_amount' => 'Importo estero', - 'existing_attachments' => 'Allegati esistenti', - 'date' => 'Data', - 'interest_date' => 'Data interesse', - 'book_date' => 'Agenda', - 'process_date' => 'Data di lavorazione', - 'category' => 'Categoria', - 'tags' => 'Etichette', - 'deletePermanently' => 'Elimina definitivamente', - 'cancel' => 'Cancella', - 'targetdate' => 'Data fine', - 'startdate' => 'Data inizio', - 'tag' => 'Etichetta', - 'under' => 'Sotto', - 'symbol' => 'Simbolo', - 'code' => 'Codice', - 'iban' => 'IBAN', - 'accountNumber' => 'Numero conto', - 'creditCardNumber' => 'Numero Carta di credito', - 'has_headers' => 'Intestazioni', - 'date_format' => 'Formato data', - 'specifix' => 'Correzioni bancarie o file specifiche', - 'attachments[]' => 'Allegati', - 'store_new_withdrawal' => 'Salva nuovo prelievo', - 'store_new_deposit' => 'Salva nuovo deposito', - 'store_new_transfer' => 'Salva nuova trasferimento', - 'add_new_withdrawal' => 'Aggiungi nuovo prelievo', - 'add_new_deposit' => 'Aggiungi nuovo deposito', - 'add_new_transfer' => 'Aggiungi nuovo giroconto', - 'title' => 'Titolo', - 'notes' => 'Note', - 'filename' => 'Nome file', - 'mime' => 'Tipo Mime', - 'size' => 'Dimensione', - 'trigger' => 'Trigger', - 'stop_processing' => 'Interrompere l\'elaborazione', - 'start_date' => 'Inizio periodo', - 'end_date' => 'Fine periodo', - 'export_start_range' => 'Inizio intervallo esportazione', - 'export_end_range' => 'Fine intervallo esportazione', - 'export_format' => 'Formato file', - 'include_attachments' => 'Includi allegati caricati', - 'include_old_uploads' => 'Includi dati importati', - 'accounts' => 'Esporta le transazioni da questi conti', - 'delete_account' => 'Elimina conto ":name"', - 'delete_bill' => 'Elimina bolletta ":name"', - 'delete_budget' => 'Elimina budget ":name"', - 'delete_category' => 'Elimina categoria ":name"', - 'delete_currency' => 'Elimina valuta ":name"', - 'delete_journal' => 'Elimina transazione con descrizione ":description"', - 'delete_attachment' => 'Elimina allegato ":name"', - 'delete_rule' => 'Elimina regola ":title"', - 'delete_rule_group' => 'Elimina gruppo regole":title"', - 'delete_link_type' => 'Elimina tipo collegamento ":name"', - 'delete_user' => 'Elimina utente ":email"', - 'user_areYouSure' => 'Se cancelli l\'utente ":email", verrà eliminato tutto. Non sarà più possibile recuperare i dati eliminati. Se cancelli te stesso, perderai l\'accesso a questa istanza di Firefly III.', - 'attachment_areYouSure' => 'Sei sicuro di voler eliminare l\'allegato ":name"?', - 'account_areYouSure' => 'Sei sicuro di voler eliminare il conto ":name"?', - 'bill_areYouSure' => 'Sei sicuro di voler eliminare il conto ":name"?', - 'rule_areYouSure' => 'Sei sicuro di voler eliminare la regola ":title"?', - 'ruleGroup_areYouSure' => 'Sei sicuro di voler eliminare il gruppo regole ":title"?', - 'budget_areYouSure' => 'Sei sicuro di voler eliminare il budget ":name"?', - 'category_areYouSure' => 'Sei sicuro di voler eliminare categoria ":name"?', - 'currency_areYouSure' => 'Sei sicuro di voler eliminare la valuta ":name"?', - 'piggyBank_areYouSure' => 'Sei sicuro di voler eliminare il salvadanaio ":name"?', - 'journal_areYouSure' => 'Sei sicuro di voler eliminare la transazione ":description"?', - 'mass_journal_are_you_sure' => 'Sei sicuro di voler eliminare queste transazioni?', - 'tag_areYouSure' => 'Sei sicuro di voler eliminare l\'etichetta ":tag"?', - 'journal_link_areYouSure' => 'Sei sicuro di voler eliminare il collegamento tra :source e :destination?', - 'linkType_areYouSure' => 'Sei sicuro di voler eliminare il tipo di collegamento ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'L\'eliminazione dei dati da Firefly III è permanente e non può essere annullata.', - 'mass_make_selection' => 'Puoi comunque impedire l\'eliminazione degli elementi rimuovendo la spunta nella casella di controllo.', - 'delete_all_permanently' => 'Elimina selezionato in modo permanente', - 'update_all_journals' => 'Aggiorna queste transazioni', - 'also_delete_transactions' => 'Bene! verrà cancellata anche l\'unica transazione connessa a questo conto. | Bene! Tutte :count le transazioni di conteggio collegate a questo conto verranno eliminate.', - 'also_delete_connections' => 'L\'unica transazione collegata a questo tipo di collegamento perderà questa connessione. | Tutto :count le transazioni di conteggio collegate a questo tipo di collegamento perderanno la connessione.', - 'also_delete_rules' => 'Anche l\'unica regola collegata a questo gruppo di regole verrà eliminata. | Tutto :count verranno eliminate anche le regole di conteggio collegate a questo gruppo di regole.', - 'also_delete_piggyBanks' => 'Verrà eliminato anche l\'unico salvadanaio collegato a questo conto. | Tutti :count il conteggio del salvadanaio collegato a questo conto verrà eliminato.', - 'bill_keep_transactions' => 'L\'unica transazione connessa a questa bolletta non verrà eliminata. | Tutte le :count transazioni del conto collegate a questa bolletta non verranno cancellate.', - 'budget_keep_transactions' => 'L\'unica transazione collegata a questo budget non verrà eliminata. | Tutte le :count transazioni del conto collegate a questo budget non verranno cancellate.', - 'category_keep_transactions' => 'L\'unica transazione collegata a questa categoria non verrà eliminata. | Tutto :count le transazioni del conto collegate a questa categoria non verranno cancellate.', - 'tag_keep_transactions' => 'L\'unica transazione connessa a questa etichetta non verrà eliminata. | Tutto :count le transazioni del conto collegate a questa etichetta non verranno cancellate.', - 'check_for_updates' => 'Controlla gli aggiornamenti', + 'amount' => 'Importo', + 'foreign_amount' => 'Importo estero', + 'existing_attachments' => 'Allegati esistenti', + 'date' => 'Data', + 'interest_date' => 'Data interesse', + 'book_date' => 'Agenda', + 'process_date' => 'Data elaborazione', + 'category' => 'Categoria', + 'tags' => 'Etichette', + 'deletePermanently' => 'Elimina definitivamente', + 'cancel' => 'Annulla', + 'targetdate' => 'Data fine', + 'startdate' => 'Data inizio', + 'tag' => 'Etichetta', + 'under' => 'Sotto', + 'symbol' => 'Simbolo', + 'code' => 'Codice', + 'iban' => 'IBAN', + 'accountNumber' => 'Numero conto', + 'creditCardNumber' => 'Numero carta di credito', + 'has_headers' => 'Intestazioni', + 'date_format' => 'Formato data', + 'specifix' => 'Correzioni bancarie o file specifiche', + 'attachments[]' => 'Allegati', + 'store_new_withdrawal' => 'Salva nuovo prelievo', + 'store_new_deposit' => 'Salva nuovo deposito', + 'store_new_transfer' => 'Salva nuova trasferimento', + 'add_new_withdrawal' => 'Aggiungi nuovo prelievo', + 'add_new_deposit' => 'Aggiungi nuovo deposito', + 'add_new_transfer' => 'Aggiungi un nuovo trasferimento', + 'title' => 'Titolo', + 'notes' => 'Note', + 'filename' => 'Nome file', + 'mime' => 'Tipo Mime', + 'size' => 'Dimensione', + 'trigger' => 'Trigger', + 'stop_processing' => 'Interrompere l\'elaborazione', + 'start_date' => 'Inizio periodo', + 'end_date' => 'Fine periodo', + 'export_start_range' => 'Data iniziale dell\'intervallo d\'esportazione', + 'export_end_range' => 'Data finale dell\'intervallo d\'esportazione', + 'export_format' => 'Formato file', + 'include_attachments' => 'Includi allegati caricati', + 'include_old_uploads' => 'Includi dati importati', + 'accounts' => 'Esporta le transazioni da questi conti', + 'delete_account' => 'Elimina conto ":name"', + 'delete_bill' => 'Elimina bolletta ":name"', + 'delete_budget' => 'Elimina budget ":name"', + 'delete_category' => 'Elimina categoria ":name"', + 'delete_currency' => 'Elimina valuta ":name"', + 'delete_journal' => 'Elimina transazione con descrizione ":description"', + 'delete_attachment' => 'Elimina allegato ":name"', + 'delete_rule' => 'Elimina regola ":title"', + 'delete_rule_group' => 'Elimina gruppo regole":title"', + 'delete_link_type' => 'Elimina tipo collegamento ":name"', + 'delete_user' => 'Elimina utente ":email"', + 'delete_recurring' => 'Elimina transazione ricorrente ":title"', + 'user_areYouSure' => 'Se cancelli l\'utente ":email", verrà eliminato tutto. Non sarà più possibile recuperare i dati eliminati. Se cancelli te stesso, perderai l\'accesso a questa istanza di Firefly III.', + 'attachment_areYouSure' => 'Sei sicuro di voler eliminare l\'allegato ":name"?', + 'account_areYouSure' => 'Sei sicuro di voler eliminare il conto ":name"?', + 'bill_areYouSure' => 'Sei sicuro di voler eliminare il conto ":name"?', + 'rule_areYouSure' => 'Sei sicuro di voler eliminare la regola ":title"?', + 'ruleGroup_areYouSure' => 'Sei sicuro di voler eliminare il gruppo regole ":title"?', + 'budget_areYouSure' => 'Sei sicuro di voler eliminare il budget ":name"?', + 'category_areYouSure' => 'Sei sicuro di voler eliminare categoria ":name"?', + 'recurring_areYouSure' => 'Sei sicuro di voler eliminare la transazione ricorrente ":title"?', + 'currency_areYouSure' => 'Sei sicuro di voler eliminare la valuta ":name"?', + 'piggyBank_areYouSure' => 'Sei sicuro di voler eliminare il salvadanaio ":name"?', + 'journal_areYouSure' => 'Sei sicuro di voler eliminare la transazione ":description"?', + 'mass_journal_are_you_sure' => 'Sei sicuro di voler eliminare queste transazioni?', + 'tag_areYouSure' => 'Sei sicuro di voler eliminare l\'etichetta ":tag"?', + 'journal_link_areYouSure' => 'Sei sicuro di voler eliminare il collegamento tra :source e :destination?', + 'linkType_areYouSure' => 'Sei sicuro di voler eliminare il tipo di collegamento ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'L\'eliminazione dei dati da Firefly III è definitiva e non può essere annullata.', + 'mass_make_selection' => 'Puoi comunque impedire l\'eliminazione degli elementi rimuovendo la spunta nella casella di controllo.', + 'delete_all_permanently' => 'Elimina selezionati definitamente', + 'update_all_journals' => 'Aggiorna queste transazioni', + 'also_delete_transactions' => 'Anche l\'unica transazione collegata a questo conto verrà eliminata.|Anche tutte le :count transazioni collegate a questo conto verranno eliminate.', + 'also_delete_connections' => 'L\'unica transazione collegata a questo tipo di collegamento perderà questa connessione. | Tutto :count le transazioni di conteggio collegate a questo tipo di collegamento perderanno la connessione.', + 'also_delete_rules' => 'Anche l\'unica regola collegata a questo gruppo di regole verrà eliminata. | Tutto :count verranno eliminate anche le regole di conteggio collegate a questo gruppo di regole.', + 'also_delete_piggyBanks' => 'Verrà eliminato anche l\'unico salvadanaio collegato a questo conto. | Tutti :count il conteggio del salvadanaio collegato a questo conto verrà eliminato.', + 'bill_keep_transactions' => 'L\'unica transazione connessa a questa bolletta non verrà eliminata.|Tutte le :count transazioni del conto collegate a questa bolletta non verranno cancellate.', + 'budget_keep_transactions' => 'L\'unica transazione collegata a questo budget non verrà eliminata.|Tutte le :count transazioni del conto collegate a questo budget non verranno cancellate.', + 'category_keep_transactions' => 'L\'unica transazione collegata a questa categoria non verrà eliminata.|Tutte le :count transazioni del conto collegate a questa categoria non verranno cancellate.', + 'recurring_keep_transactions' => 'L\'unica transazione creata da questa transazione ricorrente non verrà eliminata.|Tutte le :count transazioni create da questa transazione ricorrente non verranno eliminate.', + 'tag_keep_transactions' => 'L\'unica transazione connessa a questa etichetta non verrà eliminata.|Tutte le :count transazioni del conto collegate a questa etichetta non verranno eliminate.', + 'check_for_updates' => 'Controlla gli aggiornamenti', - 'email' => 'Indirizzo Email', + 'email' => 'Indirizzo email', 'password' => 'Password', 'password_confirmation' => 'Password (ancora)', 'blocked' => 'È bloccato?', @@ -201,13 +204,13 @@ return [ 'import_file' => 'Importa file', 'configuration_file' => 'Configurazione file', 'import_file_type' => 'Importa tipo file', - 'csv_comma' => 'A virgola (,)', - 'csv_semicolon' => 'A punto e virgola (;)', - 'csv_tab' => 'A spazio (invisibile)', + 'csv_comma' => 'Una virgola (,)', + 'csv_semicolon' => 'Un punto e virgola (;)', + 'csv_tab' => 'Una tabulazione (invisibile)', 'csv_delimiter' => 'Delimitatore campi CSV', 'csv_import_account' => 'Conto di importazione predefinito', 'csv_config' => 'Configurazione importa CSV', - 'client_id' => 'ID Client', + 'client_id' => 'Client ID', 'service_secret' => 'Servizio segreto', 'app_secret' => 'App segreto', 'app_id' => 'ID dell\'app', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Codice Nazione', 'provider_code' => 'Banca o fornitore di dati', - 'due_date' => 'Data scadenza', - 'payment_date' => 'Data pagamento', - 'invoice_date' => 'Data bolletta', - 'internal_reference' => 'Referenze interne', - 'inward' => 'Descrizione interna', - 'outward' => 'Descrizione esterna', - 'rule_group_id' => 'Gruppo regole', + 'due_date' => 'Data scadenza', + 'payment_date' => 'Data pagamento', + 'invoice_date' => 'Data fatturazione', + 'internal_reference' => 'Referenze interne', + 'inward' => 'Descrizione interna', + 'outward' => 'Descrizione esterna', + 'rule_group_id' => 'Gruppo regole', + 'transaction_description' => 'Descrizione transazione', + 'first_date' => 'Prima volta', + 'transaction_type' => 'Tipo transazione', + 'repeat_until' => 'Ripeti fino a', + 'recurring_description' => 'Descrizione transazione ricorrente', + 'repetition_type' => 'Tipo ripetizione', + 'foreign_currency_id' => 'Valuta estera', + 'repetition_end' => 'La ripetizione termina il', + 'repetitions' => 'Ripetizioni', + 'calendar' => 'Calendario', + 'weekend' => 'Fine settimana', + ]; diff --git a/resources/lang/it_IT/import.php b/resources/lang/it_IT/import.php index d91182b40d..5fd4aa332c 100644 --- a/resources/lang/it_IT/import.php +++ b/resources/lang/it_IT/import.php @@ -30,7 +30,7 @@ return [ 'prerequisites_breadcrumb_bunq' => 'Prerequisiti per bunq', 'job_configuration_breadcrumb' => 'Configurazione per ":key"', 'job_status_breadcrumb' => 'Stato di importazione per ":key"', - 'cannot_create_for_provider' => 'Firefly III non può creare un lavoro per il provider ":provider".', + 'cannot_create_for_provider' => 'Firefly III non può creare un\'operazione per il provider ":provider".', // index page: 'general_index_title' => 'Importa un file', @@ -82,7 +82,7 @@ return [ 'prerequisites_saved_for_bunq' => 'Chiave API e IP memorizzati!', // job configuration: - 'job_config_apply_rules_title' => 'Configurazione del lavoro - applicare le tue regole?', + 'job_config_apply_rules_title' => 'Configurazione dell\'operazione - applicare le tue regole?', 'job_config_apply_rules_text' => 'Una volta avviato il fornitore fittizio, le tue regole possono essere applicate alle transazioni. Questo aggiunge del tempo all\'importazione.', 'job_config_input' => 'Il tuo input', // job configuration for the fake provider: @@ -121,26 +121,29 @@ return [ 'spectre_login_status_disabled' => 'Disabilitato', 'spectre_login_new_login' => 'Accedi con un\'altra banca o con una di queste banche con credenziali diverse.', 'job_config_spectre_accounts_title' => 'Seleziona i conti dai quali importare', - 'job_config_spectre_accounts_text' => 'Hai selezionato ":name" (:country). Hai :count conti disponibili da questo fornitore. Seleziona i conti attività di Firefly III in cui devono essere memorizzate le transazioni da questi conti. Ricorda che, per importare i dati, sia l\'account Firefly III sia l\'account ":name" devono avere la stessa valuta.', + 'job_config_spectre_accounts_text' => 'Hai selezionato ":name" (:country). Hai :count conti disponibili da questo fornitore. Seleziona i conti attività di Firefly III in cui devono essere memorizzate le transazioni da questi conti. Ricorda che, per importare i dati, sia il conto di Firefly III sia il conto ":name" devono avere la stessa valuta.', 'spectre_no_supported_accounts' => 'Non puoi importare da questo conto perché le valute non corrispondono.', 'spectre_do_not_import' => '(non importare)', 'spectre_no_mapping' => 'Sembra che tu non abbia selezionato nessun account da cui importare.', 'imported_from_account' => 'Importato da ":account"', 'spectre_account_with_number' => 'Conto :number', + 'job_config_spectre_apply_rules' => 'Applica regole', + 'job_config_spectre_apply_rules_text' => 'Per impostazione predefinita le tue regole verranno applicate alle transazioni create durante questa procedura di importazione. Se non vuoi che questo accada, deseleziona questa casella di controllo.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'Account bunq', 'job_config_bunq_accounts_text' => 'Questi sono i conti associati al tuo account bunq. Seleziona i conti dai quali vuoi effettuare l\'importazione e in quale conto devono essere importate le transazioni.', - 'bunq_no_mapping' => 'Sembra che tu non abbia selezionato nessun account.', - 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', - 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'bunq_no_mapping' => 'Sembra che tu non abbia selezionato alcun conto.', + 'should_download_config' => 'Ti consigliamo di scaricare il file di configurazione per questa operazione. Ciò renderà le importazioni future più facili.', + 'share_config_file' => 'Se hai importato dati da una banca pubblica, dovresti condividere il tuo file di configurazione così da rendere più facile per gli altri utenti importare i loro dati. La condivisione del file di configurazione non espone i tuoi dettagli finanziari.', + 'job_config_bunq_apply_rules' => 'Applica regole', + 'job_config_bunq_apply_rules_text' => 'Per impostazione predefinita le tue regole verranno applicate alle transazioni create durante questa procedura di importazione. Se non vuoi che questo accada, deseleziona questa casella di controllo.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', 'spectre_extra_key_status' => 'Stato', 'spectre_extra_key_card_type' => 'Tipo carta', 'spectre_extra_key_account_name' => 'Nome conto', - 'spectre_extra_key_client_name' => 'Nome cliente', + 'spectre_extra_key_client_name' => 'Nome client', 'spectre_extra_key_account_number' => 'Numero conto', 'spectre_extra_key_blocked_amount' => 'Importo bloccato', 'spectre_extra_key_available_amount' => 'Importo disponibile', @@ -176,7 +179,7 @@ return [ 'job_config_roles_do_map_value' => 'Mappa questi valori', 'job_config_roles_no_example' => 'Nessun dato di esempio disponibile', 'job_config_roles_fa_warning' => 'Se contrassegni una colonna come contenente un importo in una valuta straniera, devi anche impostare la colonna che contiene di quale valuta si tratta.', - 'job_config_roles_rwarning' => 'Come minimo, contrassegna una colonna come colonna dell\'importo. Si consiglia di selezionare anche una colonna per la descrizione, la data e il conto opposto.', + 'job_config_roles_rwarning' => 'Come minimo contrassegna una colonna come colonna dell\'importo. Si consiglia di selezionare anche una colonna per la descrizione, la data e il conto della controparte.', 'job_config_roles_colum_count' => 'Colonna', // job config for the file provider (stage: mapping): 'job_config_map_title' => 'Configurazione di importazione (4/4) - Collega i dati importati con i dati di Firefly III', @@ -209,29 +212,29 @@ return [ // general errors and warnings: - 'bad_job_status' => 'Per accedere a questa pagina, il tuo lavoro di importazione non può avere lo stato ":status".', + 'bad_job_status' => 'Per accedere a questa pagina l\'operazione di importazione non può avere lo stato ":status".', // column roles for CSV import: 'column__ignore' => '(ignora questa colonna)', - 'column_account-iban' => 'Conto patrimonio (IBAN)', - 'column_account-id' => 'Conto patrimonio ID (matching FF3)', - 'column_account-name' => 'Conto patrimonio (nome)', + 'column_account-iban' => 'Conto attività (IBAN)', + 'column_account-id' => 'ID conto attività (mappa FF3)', + 'column_account-name' => 'Conto attività (nome)', 'column_amount' => 'Importo', 'column_amount_foreign' => 'Importo (in altra valuta)', 'column_amount_debit' => 'Importo (colonna debito)', 'column_amount_credit' => 'Importo (colonna credito)', 'column_amount-comma-separated' => 'Importo (virgola come separatore decimale)', - 'column_bill-id' => 'ID conto (matching FF3)', + 'column_bill-id' => 'ID bolletta (mappa FF3)', 'column_bill-name' => 'Nome conto', - 'column_budget-id' => 'ID budget (matching FF3)', + 'column_budget-id' => 'ID budget (mappa FF3)', 'column_budget-name' => 'Nome budget', - 'column_category-id' => 'ID Categoria (matching FF3)', + 'column_category-id' => 'ID categoria (mappa FF3)', 'column_category-name' => 'Nome categoria', 'column_currency-code' => 'Codice valuta (ISO 4217)', 'column_foreign-currency-code' => 'Codice valuta straniera (ISO 4217)', - 'column_currency-id' => 'ID valuta (matching FF3)', - 'column_currency-name' => 'Nome valuta (matching FF3)', - 'column_currency-symbol' => 'Simbolo valuta (matching FF3)', + 'column_currency-id' => 'ID valuta (mappa FF3)', + 'column_currency-name' => 'Nome valuta (mappa FF3)', + 'column_currency-symbol' => 'Simbolo valuta (mappa FF3)', 'column_date-interest' => 'Data calcolo interessi', 'column_date-book' => 'Data prenotazione della transazione', 'column_date-process' => 'Data processo della transazione', @@ -240,15 +243,15 @@ return [ 'column_date-payment' => 'Data di pagamento della transazione', 'column_date-invoice' => 'Data di fatturazione della transazione', 'column_description' => 'Descrizione', - 'column_opposing-iban' => 'Conto opposto (IBAN)', - 'column_opposing-bic' => 'Conto della controparte (BIC)', - 'column_opposing-id' => 'ID Conto opposto (matching FF3)', + 'column_opposing-iban' => 'Conto controparte (IBAN)', + 'column_opposing-bic' => 'Conto controparte (BIC)', + 'column_opposing-id' => 'ID conto controparte (mappa FF3)', 'column_external-id' => 'ID esterno', - 'column_opposing-name' => 'Conto opposto (nome)', + 'column_opposing-name' => 'Conto controparte (nome)', 'column_rabo-debit-credit' => 'Indicatore Rabo di addebito / accredito specifico della banca', 'column_ing-debit-credit' => 'Indicatore di debito / credito specifico ING', 'column_sepa-ct-id' => 'ID end-to-end del bonifico SEPA', - 'column_sepa-ct-op' => 'Conto opposto bonifico SEPA', + 'column_sepa-ct-op' => 'Identificatore SEPA conto controparte', 'column_sepa-db' => 'Addebito diretto SEPA', 'column_sepa-cc' => 'Codice Compensazione SEPA', 'column_sepa-ci' => 'Identificativo Creditore SEPA', @@ -256,9 +259,9 @@ return [ 'column_sepa-country' => 'Codice Paese SEPA', 'column_tags-comma' => 'Etichette (separate da virgola)', 'column_tags-space' => 'Etichette (separate con spazio)', - 'column_account-number' => 'Conto patrimonio (numero conto)', - 'column_opposing-number' => 'Conto opposto (numero conto)', - 'column_note' => 'Nota(e)', + 'column_account-number' => 'Conto attività (numero conto)', + 'column_opposing-number' => 'Conto controparte (numero conto)', + 'column_note' => 'Note', 'column_internal-reference' => 'Riferimento interno', ]; diff --git a/resources/lang/it_IT/intro.php b/resources/lang/it_IT/intro.php index a5b2a44c83..70721ef6e1 100644 --- a/resources/lang/it_IT/intro.php +++ b/resources/lang/it_IT/intro.php @@ -25,7 +25,7 @@ declare(strict_types=1); return [ // index 'index_intro' => 'Benvenuti nella pagina indice di Firefly III. Si prega di prendersi il tempo necessario per questa introduzione per avere un\'idea di come funziona Firefly III.', - 'index_accounts-chart' => 'Questo grafico mostra il saldo attuale dei contit risorse. Puoi selezionare gli conti visibili qui nelle tue preferenze.', + 'index_accounts-chart' => 'Questo grafico mostra il saldo attuale dei conti attività. Puoi selezionare i conti visibili qui nelle tue preferenze.', 'index_box_out_holder' => 'Questa piccola casella e le caselle accanto a questa ti daranno una rapida panoramica della tua situazione finanziaria.', 'index_help' => 'Se hai bisogno di aiuto per una pagina o un modulo, premi questo pulsante.', 'index_outro' => 'La maggior parte delle pagine di Firefly III inizieranno con un piccolo tour come questo. Vi prego di contattarci quando avete domande o commenti. Grazie!', @@ -35,7 +35,7 @@ return [ 'accounts_create_iban' => 'Dai ai tuoi conti un IBAN valido. Ciò potrebbe rendere molto facile l\'importazione dei dati in futuro.', 'accounts_create_asset_opening_balance' => 'I conti attività possono avere un "saldo di apertura", che indica l\'inizio della cronologia di questo conto in Firefly III.', 'accounts_create_asset_currency' => 'Firefly III supporta più valute. I conti attività hanno una valuta principale, che devi impostare qui.', - 'accounts_create_asset_virtual' => 'A volte può aiutare a fornire al tuo conto un saldo virtuale: un importo aggiuntivo sempre aggiunto o rimosso dal saldo effettivo.', + 'accounts_create_asset_virtual' => 'A volte può aiutare a fornire al tuo conto un saldo virtuale: un ulteriore importo sempre aggiunto o rimosso dal saldo effettivo.', // budgets index 'budgets_index_intro' => 'I budget sono usati per gestire le tue finanze e formano una delle funzioni principali di Firefly III.', @@ -59,11 +59,11 @@ return [ 'reports_report_audit_optionsBox' => 'Utilizza queste caselle di controllo per mostrare o nascondere le colonne che ti interessano.', 'reports_report_category_intro' => 'Questo resoconto ti fornirà informazioni su una o più categorie.', - 'reports_report_category_pieCharts' => 'Questi grafici ti daranno un\'idea delle spese e del reddito per categoria o per conto.', - 'reports_report_category_incomeAndExpensesChart' => 'Questo grafico mostra le tue spese e il reddito per categoria.', + 'reports_report_category_pieCharts' => 'Questi grafici ti daranno un\'idea delle spese e delle entrate per categoria o per conto.', + 'reports_report_category_incomeAndExpensesChart' => 'Questo grafico mostra le tue spese e le tue entrate per categoria.', 'reports_report_tag_intro' => 'Questo resoconto ti fornirà informazioni su uno o più etichette.', - 'reports_report_tag_pieCharts' => 'Questi grafici ti daranno un\'idea delle spese e del reddito per etichetta, conto, categoria o budget.', + 'reports_report_tag_pieCharts' => 'Questi grafici ti daranno un\'idea delle spese e delle entrate per etichetta, conto, categoria o budget.', 'reports_report_tag_incomeAndExpensesChart' => 'Questo grafico mostra le tue spese e entrate per etichetta.', 'reports_report_budget_intro' => 'Questo resoconto ti fornirà informazioni su uno o più budget.', @@ -85,7 +85,7 @@ return [ // create piggy 'piggy-banks_create_name' => 'Qual è il tuo obiettivo? Un nuovo divano, una macchina fotografica, soldi per le emergenze?', - 'piggy-banks_create_date' => 'È possibile impostare una data di destinazione o una scadenza per il salvadanaio.', + 'piggy-banks_create_date' => 'È possibile impostare una data come obiettivo o una scadenza per il salvadanaio.', // show piggy 'piggy-banks_show_piggyChart' => 'Questo grafico mostrerà lo storico di questo salvadanaio.', @@ -94,7 +94,7 @@ return [ // bill index 'bills_index_rules' => 'Qui puoi vedere quali regole verranno controllate se questa bolletta viene "toccata"', - 'bills_index_paid_in_period' => 'Questo campo indica quando il conto è stato pagato l\'ultima volta.', + 'bills_index_paid_in_period' => 'Questo campo indica quando la bolletta è stato pagata l\'ultima volta.', 'bills_index_expected_in_period' => 'Questo campo indica per ciascuna bolletta se e quando ci si aspetta che la bolletta successiva arrivi.', // show bill @@ -111,7 +111,7 @@ return [ 'bills_create_skip_holder' => 'Se una bolletta si ripete ogni 2 settimane, il campo "salta" deve essere impostato a "1" per saltare ogni volta una settimana.', // rules index - 'rules_index_intro' => 'Firefly III ti consente di gestire le regole, che verranno automaticamente applicate a qualsiasi transazione creata o modificata.', + 'rules_index_intro' => 'Firefly III ti consente di gestire delle regole che verranno automaticamente applicate a qualsiasi transazione creata o modificata.', 'rules_index_new_rule_group' => 'È possibile combinare le regole in gruppi per una gestione più semplice.', 'rules_index_new_rule' => 'Crea quante regole desideri.', 'rules_index_prio_buttons' => 'Ordinali come meglio credi.', diff --git a/resources/lang/it_IT/list.php b/resources/lang/it_IT/list.php index 4b53c93bf8..a4d4346fd7 100644 --- a/resources/lang/it_IT/list.php +++ b/resources/lang/it_IT/list.php @@ -26,21 +26,21 @@ return [ 'buttons' => 'Pulsanti', 'icon' => 'Icona', 'id' => 'ID', - 'create_date' => 'Creato a', - 'update_date' => 'Aggiornato a', - 'updated_at' => 'Aggiornato a', + 'create_date' => 'Creato il', + 'update_date' => 'Aggiornato il', + 'updated_at' => 'Aggiornato il', 'balance_before' => 'Bilancio prima', 'balance_after' => 'Bilancio dopo', 'name' => 'Nome', 'role' => 'Ruolo', - 'currentBalance' => 'Bilancio corrente', + 'currentBalance' => 'Saldo corrente', 'linked_to_rules' => 'Regole rilevanti', 'active' => 'Attivo', 'lastActivity' => 'Ultima attività', - 'balanceDiff' => 'Differenze bilancio', - 'matchesOn' => 'Abbinato', + 'balanceDiff' => 'Differenze saldi', + 'matchesOn' => 'Abbinato con', 'account_type' => 'Tipo conto', - 'created_at' => 'Creato a', + 'created_at' => 'Creato il', 'account' => 'Conto', 'matchingAmount' => 'Importo', 'split_number' => 'Diviso #', @@ -48,7 +48,7 @@ return [ 'source' => 'Sorgente', 'next_expected_match' => 'Prossimo abbinamento previsto', 'automatch' => 'Abbinamento automatico?', - 'repeat_freq' => 'Ripetizioni', + 'repeat_freq' => 'Si ripete', 'description' => 'Descrizione', 'amount' => 'Importo', 'internal_reference' => 'Referenze interne', @@ -58,7 +58,7 @@ return [ 'process_date' => 'Data lavorazione', 'due_date' => 'Data scadenza', 'payment_date' => 'Data pagamento', - 'invoice_date' => 'Data bolletta', + 'invoice_date' => 'Data fatturazione', 'interal_reference' => 'Referenze interne', 'notes' => 'Note', 'from' => 'Da', @@ -67,46 +67,46 @@ return [ 'budget' => 'Budget', 'category' => 'Categoria', 'bill' => 'Conto', - 'withdrawal' => 'Spesa', + 'withdrawal' => 'Prelievo', 'deposit' => 'Deposito', 'transfer' => 'Trasferimento', 'type' => 'Tipo', 'completed' => 'Completato', 'iban' => 'IBAN', - 'paid_current_period' => 'Pagato in questo periodo', + 'paid_current_period' => 'Pagati in questo periodo', 'email' => 'Email', - 'registered_at' => 'Registrato a', + 'registered_at' => 'Registrato il', 'is_blocked' => 'È bloccato', 'is_admin' => 'È amministratore', - 'has_two_factor' => 'Ha 2 Fattori', + 'has_two_factor' => 'Ha 2FA', 'blocked_code' => 'Codice blocco', 'source_account' => 'Conto sorgente', 'destination_account' => 'Conto destinazione', - 'accounts_count' => 'Numero conti', - 'journals_count' => 'Numero transazioni', - 'attachments_count' => 'Numero allegati', - 'bills_count' => 'Numero conti', - 'categories_count' => 'Numero categorie', - 'export_jobs_count' => 'Numero esportazioni', - 'import_jobs_count' => 'Numero importazioni', + 'accounts_count' => 'Numero di conti', + 'journals_count' => 'Numero di transazioni', + 'attachments_count' => 'Numero di allegati', + 'bills_count' => 'Numero di bollette', + 'categories_count' => 'Numero di categorie', + 'export_jobs_count' => 'Numero delle operazioni di esportazione', + 'import_jobs_count' => 'Numero delle operazioni di importazione', 'budget_count' => 'Numero di budget', - 'rule_and_groups_count' => 'Numero regole e gruppi regole', - 'tags_count' => 'Numero etichette', + 'rule_and_groups_count' => 'Numero di regole e gruppi di regole', + 'tags_count' => 'Numero di etichette', 'tags' => 'Etichette', 'inward' => 'Descrizione interna', 'outward' => 'Descrizione esterna', - 'number_of_transactions' => 'Numero transazioni', + 'number_of_transactions' => 'Numero di transazioni', 'total_amount' => 'Importo totale', 'sum' => 'Somma', 'sum_excluding_transfers' => 'Somma (esclusi i trasferimenti)', - 'sum_withdrawals' => 'Somma prelievi', + 'sum_withdrawals' => 'Somma dei prelievi', 'sum_deposits' => 'Somma versamenti', 'sum_transfers' => 'Somma dei trasferimenti', 'reconcile' => 'Riconcilia', 'account_on_spectre' => 'Conto (Spectre)', 'do_import' => 'Importo da questo conto', 'sepa-ct-id' => 'Identificativo End-To-End SEPA', - 'sepa-ct-op' => 'Identificativo Conto Oppositore SEPA', + 'sepa-ct-op' => 'Identificativo SEPA Conto Controparte', 'sepa-db' => 'Identificativo Mandato SEPA', 'sepa-country' => 'Paese SEPA', 'sepa-cc' => 'Codice Compensazione SEPA', @@ -122,5 +122,10 @@ return [ 'spectre_bank' => 'Banca', 'spectre_last_use' => 'Ultimo accesso', 'spectre_status' => 'Stato', - 'bunq_payment_id' => 'bunq payment ID', + 'bunq_payment_id' => 'ID pagamento bunq', + 'repetitions' => 'Ripetizioni', + 'title' => 'Titolo', + 'transaction_s' => 'Transazioni', + 'field' => 'Campo', + 'value' => 'Valore', ]; diff --git a/resources/lang/it_IT/passwords.php b/resources/lang/it_IT/passwords.php index 3e44682679..5261554c24 100644 --- a/resources/lang/it_IT/passwords.php +++ b/resources/lang/it_IT/passwords.php @@ -24,9 +24,9 @@ declare(strict_types=1); return [ 'password' => 'Le password devono contenere almeno sei caratteri e devono corrispondere alla conferma.', - 'user' => 'Non possiamo trovare un utente con questo indirizzo e-mail.', + 'user' => 'Non riusciamo a trovare un utente con questo indirizzo e-mail.', 'token' => 'Questo token di reimpostazione della password non è valido.', - 'sent' => 'Abbiamo inviato via e-mail il tuo link per la reimpostazione della password!', + 'sent' => 'Abbiamo inviato via e-mail il link per la reimpostazione della password!', 'reset' => 'La tua password è stata resettata!', 'blocked' => 'Riprova.', ]; diff --git a/resources/lang/it_IT/validation.php b/resources/lang/it_IT/validation.php index f6c286443a..c5d8699a6b 100644 --- a/resources/lang/it_IT/validation.php +++ b/resources/lang/it_IT/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Questo non è un IBAN valido.', - 'source_equals_destination' => 'Il conto di origine è uguale al conto di destinazione', + 'source_equals_destination' => 'Il conto di origine è uguale al conto di destinazione.', 'unique_account_number_for_user' => 'Sembra che questo numero di conto sia già in uso.', 'unique_iban_for_user' => 'Sembra che questo IBAN sia già in uso.', 'deleted_user' => 'A causa dei vincoli di sicurezza, non è possibile registrarsi utilizzando questo indirizzo email.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'File caricato con successo ":name".', 'must_exist' => 'L\'ID nel campo :attribute non esiste nel database.', 'all_accounts_equal' => 'Tutti i conti in questo campo devono essere uguali.', - 'invalid_selection' => 'La tua selezione non è valida', + 'invalid_selection' => 'La tua selezione non è valida.', 'belongs_user' => 'Questo valore non è valido per questo campo.', 'at_least_one_transaction' => 'Hai bisogno di almeno una transazione.', + 'at_least_one_repetition' => 'È necessaria almeno una ripetizione.', + 'require_repeat_until' => 'Richiede un numero di ripetizioni o una data di fine (ripeti fino al), non entrambi.', 'require_currency_info' => 'Il contenuto di questo campo non è valido senza informazioni sulla valuta.', 'equal_description' => 'La descrizione della transazione non deve essere uguale alla descrizione globale.', 'file_invalid_mime' => 'Il file ":name" è di tipo ":mime" che non è accettato come nuovo caricamento.', 'file_too_large' => 'Il file ":name" è troppo grande.', - 'belongs_to_user' => 'Il valore di :attribute è sconosciuto', + 'belongs_to_user' => 'Il valore di :attribute è sconosciuto.', 'accepted' => 'L\' :attribute deve essere accettato.', 'bic' => 'Questo non è un BIC valido.', + 'at_least_one_trigger' => 'Una regola deve avere almeno un trigger.', + 'at_least_one_action' => 'Una regola deve avere almeno una azione.', + 'base64' => 'Questi non sono dati codificati in base64 validi.', + 'model_id_invalid' => 'L\'ID fornito sembra non essere valido per questo modello.', 'more' => ':attribute deve essere maggiore di zero.', 'active_url' => ':attribute non è un URL valido.', 'after' => ':attribute deve essere una data dopo :date.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute deve essere una matrice.', 'unique_for_user' => 'C\'è già una voce con questo :attribute.', 'before' => ':attribute deve essere una data prima :date.', - 'unique_object_for_user' => 'Questo nome è già in uso', - 'unique_account_for_user' => 'Questo nome conto è già in uso', + 'unique_object_for_user' => 'Questo nome è già in uso.', + 'unique_account_for_user' => 'Il nome del conto è già in uso.', 'between.numeric' => ':attribute con questo nome conto è già in uso :min e :max.', 'between.file' => ':attribute deve essere :min e :max kilobyte.', 'between.string' => ':attribute deve essere tra :min e :max caratteri.', @@ -67,7 +73,7 @@ return [ 'digits' => ':attribute deve essere :digits cifre.', 'digits_between' => ':attribute deve essere :min e :max cifre.', 'email' => ':attribute deve essere un indirizzo email valido.', - 'filled' => ':attribute il campo è obbligatiorio.', + 'filled' => 'Il campo :attribute è obbligatorio.', 'exists' => ':attribute selezionato non è valido.', 'image' => ':attribute deve essere un\'immagine.', 'in' => ':attribute selezionato non è valido.', @@ -85,14 +91,17 @@ return [ 'min.array' => ':attribute deve avere almeno :min voci.', 'not_in' => ':attribute selezionato è invalido.', 'numeric' => ':attribute deve essere un numero.', + 'numeric_native' => 'L\'importo nativo deve essere un numero.', + 'numeric_destination' => 'L\'importo di destinazione deve essere un numero.', + 'numeric_source' => 'L\'importo sorgente deve essere un numero.', 'regex' => ':attribute formato non valido', - 'required' => ':attribute il campo è obbligatiorio.', - 'required_if' => ':attribute il campo è richiesto quando :other is :value.', - 'required_unless' => ':attribute il campo è richiesto a meno che :other è in :values.', - 'required_with' => ':attribute il campo è richiesto quando :values è presente.', - 'required_with_all' => ':attribute il campo è richiesto quando :values è presente.', - 'required_without' => ':attribute il campo è richiesto quando :values non è presente.', - 'required_without_all' => ':attribute il campo è richiesto quando nessuno di :values sono presenti.', + 'required' => 'Il campo :attribute è obbligatorio.', + 'required_if' => 'Il campo :attribute è obbligatorio quando :other è :value.', + 'required_unless' => 'Il campo :attribute è obbligatorio a meno che :other è in :values.', + 'required_with' => 'Il campo :attribute è obbligatorio quando :values è presente.', + 'required_with_all' => 'Il campo :attribute è obbligatorio quando :values è presente.', + 'required_without' => 'Il campo :attribute è obbligatorio quando :values non è presente.', + 'required_without_all' => 'Il campo :attribute è obbligatorio quando nessuno di :values è presente.', 'same' => ':attribute e :other deve combaciare.', 'size.numeric' => ':attribute deve essere :size.', 'amount_min_over_max' => 'L\'importo minimo non può essere maggiore dell\'importo massimo.', @@ -103,15 +112,18 @@ return [ 'string' => ':attribute deve essere una stringa.', 'url' => ':attribute il formato non è valido.', 'timezone' => ':attribute deve essere una zona valida.', - '2fa_code' => ':attribute il campo non è valido.', + '2fa_code' => 'Il campo :attribute non è valido.', 'dimensions' => ':attribute ha dimensioni di immagine non valide.', 'distinct' => ':attribute il campo ha un valore doppio.', 'file' => ':attribute deve essere un file.', 'in_array' => ':attribute il campo non esiste in :other.', 'present' => ':attribute il campo deve essere presente.', - 'amount_zero' => 'L\'importo totale non può essere zero', - 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'Questa non è una password sicura. Per favore riprova. Per ulteriori informazioni, visitare http://bit.ly/FF3-password-security', + 'amount_zero' => 'L\'importo totale non può essere zero.', + 'unique_piggy_bank_for_user' => 'Il nome del salvadanaio deve essere unico.', + 'secure_password' => 'Questa non è una password sicura. Riprova. Per maggiori informazioni visita http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Il tipo di ripetizione della transazione ricorrente non è valido.', + 'valid_recurrence_rep_moment' => 'Il momento di ripetizione per questo tipo di ripetizione non è valido.', + 'invalid_account_info' => 'Informazione sul conto non valida.', 'attributes' => [ 'email' => 'indirizzo email', 'description' => 'descrizione', @@ -119,9 +131,9 @@ return [ 'name' => 'nome', 'piggy_bank_id' => 'ID salvadanaio', 'targetamount' => 'importo obiettivo', - 'openingBalanceDate' => 'data bilancio apertura', + 'openingBalanceDate' => 'data saldo di apertura', 'openingBalance' => 'saldo apertura', - 'match' => 'partita', + 'match' => 'abbinamento', 'amount_min' => 'importo minimo', 'amount_max' => 'importo massimo', 'title' => 'titolo', diff --git a/resources/lang/nl_NL/config.php b/resources/lang/nl_NL/config.php index d2e66fcf86..aab20ae1cb 100644 --- a/resources/lang/nl_NL/config.php +++ b/resources/lang/nl_NL/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'nl', - 'locale' => 'nl, Dutch, nl_NL, nl_NL.utf8, nl_NL.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'week %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'D MMMM YYYY', - 'date_time_js' => 'D MMMM YYYY @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'nl', + 'locale' => 'nl, Dutch, nl_NL, nl_NL.utf8, nl_NL.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %e %B %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'week %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'D MMMM YYYY', + 'date_time_js' => 'D MMMM YYYY @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Maandag', + 'dow_2' => 'Dinsdag', + 'dow_3' => 'Woensdag', + 'dow_4' => 'Donderdag', + 'dow_5' => 'Vrijdag', + 'dow_6' => 'Zaterdag', + 'dow_7' => 'Zondag', ]; diff --git a/resources/lang/nl_NL/demo.php b/resources/lang/nl_NL/demo.php index 5f6ed1f0b2..d8b739c605 100644 --- a/resources/lang/nl_NL/demo.php +++ b/resources/lang/nl_NL/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Deze uitgaven, inkomsten en overschrijvingen zijn niet heel fantasierijk. Ze zijn automatisch gegenereerd.', 'piggy-banks-index' => 'Zoals je kan zien zijn er drie spaarpotjes. Gebruik de plus- en minknoppen om het bedrag in de spaarpotjes te veranderen. Klik op de naam van het spaarpotje om er de geschiedenis van te zien.', 'import-index' => 'Je kan elk CSV bestand importeren met Firefly III. Het is ook mogelijk om transacties te importeren van bunq en Spectre. Andere verzamelbedrijven worden ook geïntegreerd. De demo-gebruiker mag alleen de "nep"-importhulp gebruiken. Deze genereert een paar willekeurige transacties om te laten zien hoe het werkt.', + 'recurring-index' => 'Denk er aan dat deze functie nog ontwikkeld wordt en misschien niet lekker werkt.', + 'recurring-create' => 'Denk er aan dat deze functie nog ontwikkeld wordt en misschien niet lekker werkt.', ]; diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 051852c28d..6e4acdc31b 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Scan deze QR code met een app op je telefoon (zoals Authy of Google Authenticator). Vul de code die je terug krijgt hier in.', 'pref_two_factor_auth_reset_code' => 'Reset de verificatiecode', 'pref_two_factor_auth_disable_2fa' => '2FA uitzetten', + '2fa_use_secret_instead' => 'Als je de QR code niet kan scannen gebruik dan de geheime code: :secret.', 'pref_save_settings' => 'Instellingen opslaan', 'saved_preferences' => 'Voorkeuren opgeslagen!', 'preferences_general' => 'Algemeen', @@ -820,7 +821,7 @@ return [ 'language' => 'Taal', 'new_savings_account' => ':bank_name spaarrekening', 'cash_wallet' => 'Cash-rekening', - 'currency_not_present' => 'Geen zorgen als de valuta die je gewend bent er niet tussen staat. Je kan je eigen valuta maken onder Opties > Valuta.', + 'currency_not_present' => 'Geen zorgen als de valuta die je gewend bent er niet tussen staat. Je kan je eigen valuta maken onder Opties > Valuta.', // home page: 'yourAccounts' => 'Je betaalrekeningen', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Saldo aan het einde van de periode', 'splitByAccount' => 'Per betaalrekening', 'coveredWithTags' => 'Gecorrigeerd met tags', - 'leftUnbalanced' => 'Ongecorrigeerd', 'leftInBudget' => 'Over van budget', 'sumOfSums' => 'Alles bij elkaar', 'noCategory' => '(zonder categorie)', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => 'wordt (deels) terugbetaald door', 'is (partially) paid for by_inward' => 'wordt (deels) betaald door', 'is (partially) reimbursed by_inward' => 'wordt (deels) vergoed door', + 'inward_transaction' => 'Actieve transactie', + 'outward_transaction' => 'Passieve transactie', 'relates to_outward' => 'gerelateerd aan', '(partially) refunds_outward' => 'is een (gedeeltelijke) terugbetaling voor', '(partially) pays for_outward' => 'betaalt (deels) voor', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Kan geen gesplitste transactie omzetten', // Import page (general strings only) - 'import_index_title' => 'Gegevens importeren in Firefly III', + 'import_index_title' => 'Transacties importeren in Firefly III', 'import_data' => 'Importeer data', + 'import_transactions' => 'Importeer transacties', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Deze functie werkt niet als je Firefly III gebruikt in combinatie met Sandstorm.IO.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Je hebt nog geen contracten. Je kan contracten gebruiken om terugkerende uitgaven bij te houden, zoals de huur of verzekeringen.', 'no_bills_imperative_default' => 'Heb je zulke uitgaven? Maak dan een contract en houd de betalingen bij:', 'no_bills_create_default' => 'Maak een contract', + + // recurring transactions + 'recurrences' => 'Periodieke transacties', + 'no_recurring_title_default' => 'Maak een periodieke transactie!', + 'no_recurring_intro_default' => 'Je hebt nog geen periodieke transacties. Je kan deze gebruiken om er voor te zorgen dat Firefly III automatisch nieuwe transacties voor je maakt.', + 'no_recurring_imperative_default' => 'Dit is een behoorlijk geadvanceerde functie maar het kan vreselijk handig zijn. Lees ook zeker de documentatie (?)-icoontje rechtsboven) voor je verder gaat.', + 'no_recurring_create_default' => 'Maak een periodieke transactie', + 'make_new_recurring' => 'Maak een periodieke transactie', + 'recurring_daily' => 'Elke dag', + 'recurring_weekly' => 'Elke week op :weekday', + 'recurring_monthly' => 'Elke maand op de :dayOfMonth(e) dag', + 'recurring_ndom' => 'Elke maand op de :dayOfMonth(e) :weekday', + 'recurring_yearly' => 'Elk jaar op :date', + 'overview_for_recurrence' => 'Overzicht voor periodieke transactie ":title"', + 'warning_duplicates_repetitions' => 'Soms zie je hier datums dubbel staan. Dat kan als meerdere herhalingen in elkaars vaarwater zitten. Firefly III maakt altijd maar één transactie per dag.', + 'created_transactions' => 'Gerelateerde transacties', + 'expected_Withdrawals' => 'Verwachte uitgaven', + 'expected_Deposits' => 'Verwachte inkomsten', + 'expected_Transfers' => 'Verwachte overschrijvingen', + 'created_Withdrawals' => 'Gemaakte uitgaven', + 'created_Deposits' => 'Gemaakte inkomsten', + 'created_Transfers' => 'Gemaakte overschrijvingen', + 'created_from_recurrence' => 'Gemaakt door periodieke transactie ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notities', + 'recurring_meta_field_bill_id' => 'Contract', + 'recurring_meta_field_piggy_bank_id' => 'Spaarpotje', + 'create_new_recurrence' => 'Maak een nieuwe periodieke transactie', + 'help_first_date' => 'Geef aan wanneer je de eerste transactie verwacht. Dit moet in de toekomst zijn.', + 'help_first_date_no_past' => 'Geef aan wanneer je de eerste transactie verwacht. Firefly III zal geen transacties in het verleden maken.', + 'no_currency' => '(geen valuta)', + 'mandatory_for_recurring' => 'Verplichte periodieke informatie', + 'mandatory_for_transaction' => 'Verplichte transactieinformatie', + 'optional_for_recurring' => 'Optionele periodieke informatie', + 'optional_for_transaction' => 'Optionele transactieinformatie', + 'change_date_other_options' => 'Wijzig de "eerste datum" om meer opties te zien.', + 'mandatory_fields_for_tranaction' => 'De waarden die je hier invult worden gebruikt om de transactie(s) te maken', + 'click_for_calendar' => 'Hier vind je een kalender die laat zien wanneer de transactie zal herhalen.', + 'repeat_forever' => 'Voor altijd herhalen', + 'repeat_until_date' => 'Herhalen tot een datum', + 'repeat_times' => 'Een aantal maal herhalen', + 'recurring_skips_one' => 'Elke tweede', + 'recurring_skips_more' => 'Slaat :count keer over', + 'store_new_recurrence' => 'Sla periodieke transactie op', + 'stored_new_recurrence' => 'Periodieke transactie ":title" is opgeslagen.', + 'edit_recurrence' => 'Wijzig periodieke transactie ":title"', + 'recurring_repeats_until' => 'Herhaalt tot :date', + 'recurring_repeats_forever' => 'Blijft herhalen', + 'recurring_repeats_x_times' => 'Herhaalt :count keer', + 'update_recurrence' => 'Wijzig periodieke transactie', + 'updated_recurrence' => 'Periodieke transactie ":title" is gewijzigd', + 'recurrence_is_inactive' => 'Deze periodieke transactie is niet actief en maakt geen transacties aan.', + 'delete_recurring' => 'Verwijder periodieke transactie ":title"', + 'new_recurring_transaction' => 'Nieuwe periodieke transactie', + 'help_weekend' => 'Wat moet Firefly III doen als de periodieke transactie in het weekend valt?', + 'do_nothing' => 'Gewoon transactie maken', + 'skip_transaction' => 'Transactie overslaan', + 'jump_to_friday' => 'Transactie maken op de vrijdag ervoor', + 'jump_to_monday' => 'Transactie maken op de maandag erna', + 'will_jump_friday' => 'Wordt op de vrijdag ervoor gemaakt i.p.v. in het weekend.', + 'will_jump_monday' => 'Wordt op de maandag erna gemaakt i. p. v. in het weekend.', + 'except_weekends' => 'Behalve de weekenden', + 'recurrence_deleted' => 'Periodieke transactie ":title" verwijderd', ]; diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index 748af68a19..f19ff53daa 100644 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Banknaam', - 'bank_balance' => 'Saldo', - 'savings_balance' => 'Saldo van spaarrekening', - 'credit_card_limit' => 'Credit card limiet', - 'automatch' => 'Automatisch herkennen', - 'skip' => 'Overslaan', - 'name' => 'Naam', - 'active' => 'Actief', - 'amount_min' => 'Minimumbedrag', - 'amount_max' => 'Maximumbedrag', - 'match' => 'Reageert op', - 'strict' => 'Strikte modus', - 'repeat_freq' => 'Herhaling', - 'journal_currency_id' => 'Valuta', - 'currency_id' => 'Valuta', - 'transaction_currency_id' => 'Valuta', - 'external_ip' => 'Het externe IP-adres van je server', - 'attachments' => 'Bijlagen', - 'journal_amount' => 'Bedrag', - 'journal_source_account_name' => 'Debiteur (bron)', - 'journal_source_account_id' => 'Betaalrekening (bron)', - 'BIC' => 'BIC', - 'verify_password' => 'Bevestig wachtwoordsterkte', - 'source_account' => 'Bronrekening', - 'destination_account' => 'Doelrekening', - 'journal_destination_account_id' => 'Betaalrekening (doel)', - 'asset_destination_account' => 'Betaalrekening (doel)', - 'asset_source_account' => 'Betaalrekening (bron)', - 'journal_description' => 'Omschrijving', - 'note' => 'Notities', - 'split_journal' => 'Splits deze transactie', - 'split_journal_explanation' => 'Splits deze transactie in meerdere stukken', - 'currency' => 'Valuta', - 'account_id' => 'Betaalrekening', - 'budget_id' => 'Budget', - 'openingBalance' => 'Startsaldo', - 'tagMode' => 'Tag modus', - 'tag_position' => 'Taglocatie', - 'virtualBalance' => 'Virtuele saldo', - 'targetamount' => 'Doelbedrag', - 'accountRole' => 'Rol van rekening', - 'openingBalanceDate' => 'Startsaldodatum', - 'ccType' => 'Betaalplan', - 'ccMonthlyPaymentDate' => 'Betaaldatum', - 'piggy_bank_id' => 'Spaarpotje', - 'returnHere' => 'Keer terug', - 'returnHereExplanation' => 'Terug naar deze pagina na het opslaan.', - 'returnHereUpdateExplanation' => 'Terug naar deze pagina na het wijzigen.', - 'description' => 'Omschrijving', - 'expense_account' => 'Crediteur', - 'revenue_account' => 'Debiteur', - 'decimal_places' => 'Aantal decimalen', - 'exchange_rate_instruction' => 'Vreemde valuta', - 'source_amount' => 'Bronbedrag', - 'destination_amount' => 'Doelbedrag', - 'native_amount' => 'Origineel bedrag', - 'new_email_address' => 'Nieuw emailadres', - 'verification' => 'Verificatie', - 'api_key' => 'API sleutel', - 'remember_me' => 'Aangemeld blijven', + 'bank_name' => 'Banknaam', + 'bank_balance' => 'Saldo', + 'savings_balance' => 'Saldo van spaarrekening', + 'credit_card_limit' => 'Credit card limiet', + 'automatch' => 'Automatisch herkennen', + 'skip' => 'Overslaan', + 'name' => 'Naam', + 'active' => 'Actief', + 'amount_min' => 'Minimumbedrag', + 'amount_max' => 'Maximumbedrag', + 'match' => 'Reageert op', + 'strict' => 'Strikte modus', + 'repeat_freq' => 'Herhaling', + 'journal_currency_id' => 'Valuta', + 'currency_id' => 'Valuta', + 'transaction_currency_id' => 'Valuta', + 'external_ip' => 'Het externe IP-adres van je server', + 'attachments' => 'Bijlagen', + 'journal_amount' => 'Bedrag', + 'journal_source_name' => 'Debiteur (bron)', + 'journal_source_id' => 'Betaalrekening (bron)', + 'BIC' => 'BIC', + 'verify_password' => 'Bevestig wachtwoordsterkte', + 'source_account' => 'Bronrekening', + 'destination_account' => 'Doelrekening', + 'journal_destination_id' => 'Betaalrekening (doel)', + 'asset_destination_account' => 'Betaalrekening (doel)', + 'asset_source_account' => 'Betaalrekening (bron)', + 'journal_description' => 'Omschrijving', + 'note' => 'Notities', + 'split_journal' => 'Splits deze transactie', + 'split_journal_explanation' => 'Splits deze transactie in meerdere stukken', + 'currency' => 'Valuta', + 'account_id' => 'Betaalrekening', + 'budget_id' => 'Budget', + 'openingBalance' => 'Startsaldo', + 'tagMode' => 'Tag modus', + 'tag_position' => 'Taglocatie', + 'virtualBalance' => 'Virtuele saldo', + 'targetamount' => 'Doelbedrag', + 'accountRole' => 'Rol van rekening', + 'openingBalanceDate' => 'Startsaldodatum', + 'ccType' => 'Betaalplan', + 'ccMonthlyPaymentDate' => 'Betaaldatum', + 'piggy_bank_id' => 'Spaarpotje', + 'returnHere' => 'Keer terug', + 'returnHereExplanation' => 'Terug naar deze pagina na het opslaan.', + 'returnHereUpdateExplanation' => 'Terug naar deze pagina na het wijzigen.', + 'description' => 'Omschrijving', + 'expense_account' => 'Crediteur', + 'revenue_account' => 'Debiteur', + 'decimal_places' => 'Aantal decimalen', + 'exchange_rate_instruction' => 'Vreemde valuta', + 'source_amount' => 'Bronbedrag', + 'destination_amount' => 'Doelbedrag', + 'native_amount' => 'Origineel bedrag', + 'new_email_address' => 'Nieuw emailadres', + 'verification' => 'Verificatie', + 'api_key' => 'API sleutel', + 'remember_me' => 'Aangemeld blijven', 'source_account_asset' => 'Bronrekening (betaalrekening)', 'destination_account_expense' => 'Doelrekening (crediteur)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Verander inkomsten', 'convert_Transfer' => 'Verander overschrijving', - 'amount' => 'Bedrag', - 'foreign_amount' => 'Bedrag in vreemde valuta', - 'existing_attachments' => 'Bestaande bijlagen', - 'date' => 'Datum', - 'interest_date' => 'Rentedatum', - 'book_date' => 'Boekdatum', - 'process_date' => 'Verwerkingsdatum', - 'category' => 'Categorie', - 'tags' => 'Tags', - 'deletePermanently' => 'Verwijderen', - 'cancel' => 'Annuleren', - 'targetdate' => 'Doeldatum', - 'startdate' => 'Startdatum', - 'tag' => 'Tag', - 'under' => 'Onder', - 'symbol' => 'Symbool', - 'code' => 'Code', - 'iban' => 'IBAN', - 'accountNumber' => 'Rekeningnummer', - 'creditCardNumber' => 'Creditcardnummer', - 'has_headers' => 'Kolomnamen op de eerste rij?', - 'date_format' => 'Datumformaat', - 'specifix' => 'Bank- or of bestandsspecifieke opties', - 'attachments[]' => 'Bijlagen', - 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', - 'store_new_deposit' => 'Nieuwe inkomsten opslaan', - 'store_new_transfer' => 'Nieuwe overschrijving opslaan', - 'add_new_withdrawal' => 'Maak nieuwe uitgave', - 'add_new_deposit' => 'Maak nieuwe inkomsten', - 'add_new_transfer' => 'Maak nieuwe overschrijving', - 'title' => 'Titel', - 'notes' => 'Notities', - 'filename' => 'Bestandsnaam', - 'mime' => 'Bestandstype', - 'size' => 'Grootte', - 'trigger' => 'Trigger', - 'stop_processing' => 'Stop met verwerken', - 'start_date' => 'Start van bereik', - 'end_date' => 'Einde van bereik', - 'export_start_range' => 'Start van exportbereik', - 'export_end_range' => 'Einde van exportbereik', - 'export_format' => 'Bestandsformaat', - 'include_attachments' => 'Sla ook geüploade bijlagen op', - 'include_old_uploads' => 'Sla ook geïmporteerde bestanden op', - 'accounts' => 'Exporteer boekingen van deze rekeningen', - 'delete_account' => 'Verwijder rekening ":name"', - 'delete_bill' => 'Verwijder contract ":name"', - 'delete_budget' => 'Verwijder budget ":name"', - 'delete_category' => 'Verwijder categorie ":name"', - 'delete_currency' => 'Verwijder valuta ":name"', - 'delete_journal' => 'Verwijder transactie met omschrijving ":description"', - 'delete_attachment' => 'Verwijder bijlage ":name"', - 'delete_rule' => 'Verwijder regel ":title"', - 'delete_rule_group' => 'Verwijder regelgroep ":title"', - 'delete_link_type' => 'Verwijder linktype ":name"', - 'delete_user' => 'Verwijder gebruiker ":email"', - 'user_areYouSure' => 'Als je gebruiker ":email" verwijdert is alles weg. Je kan dit niet ongedaan maken of ont-verwijderen of wat dan ook. Als je jezelf verwijdert ben je ook je toegang tot deze installatie van Firefly III kwijt.', - 'attachment_areYouSure' => 'Weet je zeker dat je de bijlage met naam ":name" wilt verwijderen?', - 'account_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?', - 'bill_areYouSure' => 'Weet je zeker dat je het contract met naam ":name" wilt verwijderen?', - 'rule_areYouSure' => 'Weet je zeker dat je regel ":title" wilt verwijderen?', - 'ruleGroup_areYouSure' => 'Weet je zeker dat je regelgroep ":title" wilt verwijderen?', - 'budget_areYouSure' => 'Weet je zeker dat je het budget met naam ":name" wilt verwijderen?', - 'category_areYouSure' => 'Weet je zeker dat je het category met naam ":name" wilt verwijderen?', - 'currency_areYouSure' => 'Weet je zeker dat je de valuta met naam ":name" wilt verwijderen?', - 'piggyBank_areYouSure' => 'Weet je zeker dat je het spaarpotje met naam ":name" wilt verwijderen?', - 'journal_areYouSure' => 'Weet je zeker dat je de transactie met naam ":description" wilt verwijderen?', - 'mass_journal_are_you_sure' => 'Weet je zeker dat je al deze transacties wilt verwijderen?', - 'tag_areYouSure' => 'Weet je zeker dat je de tag met naam ":tag" wilt verwijderen?', - 'journal_link_areYouSure' => 'Weet je zeker dat je de koppeling tussen :source en :destination wilt verwijderen?', - 'linkType_areYouSure' => 'Weet je zeker dat je linktype ":name" (":inward" / ":outward") wilt verwijderen?', - 'permDeleteWarning' => 'Dingen verwijderen uit Firefly III is permanent en kan niet ongedaan gemaakt worden.', - 'mass_make_selection' => 'Je kan items alsnog redden van de ondergang door het vinkje weg te halen.', - 'delete_all_permanently' => 'Verwijder geselecteerde items permanent', - 'update_all_journals' => 'Wijzig deze transacties', - 'also_delete_transactions' => 'Ook de enige transactie verbonden aan deze rekening wordt verwijderd.|Ook alle :count transacties verbonden aan deze rekening worden verwijderd.', - 'also_delete_connections' => 'De enige transactie gelinkt met dit linktype zal deze verbinding verliezen. | Alle :count transacties met dit linktype zullen deze verbinding verliezen.', - 'also_delete_rules' => 'De enige regel in deze regelgroep wordt ook verwijderd.|Alle :count regels in deze regelgroep worden ook verwijderd.', - 'also_delete_piggyBanks' => 'Ook het spaarpotje verbonden aan deze rekening wordt verwijderd.|Ook alle :count spaarpotjes verbonden aan deze rekening worden verwijderd.', - 'bill_keep_transactions' => 'De transactie verbonden aan dit contract blijft bewaard.|De :count transacties verbonden aan dit contract blijven bewaard.', - 'budget_keep_transactions' => 'De transactie verbonden aan dit budget blijft bewaard.|De :count transacties verbonden aan dit budget blijven bewaard.', - 'category_keep_transactions' => 'De transactie verbonden aan deze categorie blijft bewaard.|De :count transacties verbonden aan deze categorie blijven bewaard.', - 'tag_keep_transactions' => 'De transactie verbonden aan deze tag blijft bewaard.|De :count transacties verbonden aan deze tag blijven bewaard.', - 'check_for_updates' => 'Op updates controleren', + 'amount' => 'Bedrag', + 'foreign_amount' => 'Bedrag in vreemde valuta', + 'existing_attachments' => 'Bestaande bijlagen', + 'date' => 'Datum', + 'interest_date' => 'Rentedatum', + 'book_date' => 'Boekdatum', + 'process_date' => 'Verwerkingsdatum', + 'category' => 'Categorie', + 'tags' => 'Tags', + 'deletePermanently' => 'Verwijderen', + 'cancel' => 'Annuleren', + 'targetdate' => 'Doeldatum', + 'startdate' => 'Startdatum', + 'tag' => 'Tag', + 'under' => 'Onder', + 'symbol' => 'Symbool', + 'code' => 'Code', + 'iban' => 'IBAN', + 'accountNumber' => 'Rekeningnummer', + 'creditCardNumber' => 'Creditcardnummer', + 'has_headers' => 'Kolomnamen op de eerste rij?', + 'date_format' => 'Datumformaat', + 'specifix' => 'Bank- or of bestandsspecifieke opties', + 'attachments[]' => 'Bijlagen', + 'store_new_withdrawal' => 'Nieuwe uitgave opslaan', + 'store_new_deposit' => 'Nieuwe inkomsten opslaan', + 'store_new_transfer' => 'Nieuwe overschrijving opslaan', + 'add_new_withdrawal' => 'Maak nieuwe uitgave', + 'add_new_deposit' => 'Maak nieuwe inkomsten', + 'add_new_transfer' => 'Maak nieuwe overschrijving', + 'title' => 'Titel', + 'notes' => 'Notities', + 'filename' => 'Bestandsnaam', + 'mime' => 'Bestandstype', + 'size' => 'Grootte', + 'trigger' => 'Trigger', + 'stop_processing' => 'Stop met verwerken', + 'start_date' => 'Start van bereik', + 'end_date' => 'Einde van bereik', + 'export_start_range' => 'Start van exportbereik', + 'export_end_range' => 'Einde van exportbereik', + 'export_format' => 'Bestandsformaat', + 'include_attachments' => 'Sla ook geüploade bijlagen op', + 'include_old_uploads' => 'Sla ook geïmporteerde bestanden op', + 'accounts' => 'Exporteer boekingen van deze rekeningen', + 'delete_account' => 'Verwijder rekening ":name"', + 'delete_bill' => 'Verwijder contract ":name"', + 'delete_budget' => 'Verwijder budget ":name"', + 'delete_category' => 'Verwijder categorie ":name"', + 'delete_currency' => 'Verwijder valuta ":name"', + 'delete_journal' => 'Verwijder transactie met omschrijving ":description"', + 'delete_attachment' => 'Verwijder bijlage ":name"', + 'delete_rule' => 'Verwijder regel ":title"', + 'delete_rule_group' => 'Verwijder regelgroep ":title"', + 'delete_link_type' => 'Verwijder linktype ":name"', + 'delete_user' => 'Verwijder gebruiker ":email"', + 'delete_recurring' => 'Periodieke transactie ":title" verwijderen', + 'user_areYouSure' => 'Als je gebruiker ":email" verwijdert is alles weg. Je kan dit niet ongedaan maken of ont-verwijderen of wat dan ook. Als je jezelf verwijdert ben je ook je toegang tot deze installatie van Firefly III kwijt.', + 'attachment_areYouSure' => 'Weet je zeker dat je de bijlage met naam ":name" wilt verwijderen?', + 'account_areYouSure' => 'Weet je zeker dat je de rekening met naam ":name" wilt verwijderen?', + 'bill_areYouSure' => 'Weet je zeker dat je het contract met naam ":name" wilt verwijderen?', + 'rule_areYouSure' => 'Weet je zeker dat je regel ":title" wilt verwijderen?', + 'ruleGroup_areYouSure' => 'Weet je zeker dat je regelgroep ":title" wilt verwijderen?', + 'budget_areYouSure' => 'Weet je zeker dat je het budget met naam ":name" wilt verwijderen?', + 'category_areYouSure' => 'Weet je zeker dat je het category met naam ":name" wilt verwijderen?', + 'recurring_areYouSure' => 'Weet je zeker dat je periodieke transactie ":title" wilt verwijderen?', + 'currency_areYouSure' => 'Weet je zeker dat je de valuta met naam ":name" wilt verwijderen?', + 'piggyBank_areYouSure' => 'Weet je zeker dat je het spaarpotje met naam ":name" wilt verwijderen?', + 'journal_areYouSure' => 'Weet je zeker dat je de transactie met naam ":description" wilt verwijderen?', + 'mass_journal_are_you_sure' => 'Weet je zeker dat je al deze transacties wilt verwijderen?', + 'tag_areYouSure' => 'Weet je zeker dat je de tag met naam ":tag" wilt verwijderen?', + 'journal_link_areYouSure' => 'Weet je zeker dat je de koppeling tussen :source en :destination wilt verwijderen?', + 'linkType_areYouSure' => 'Weet je zeker dat je linktype ":name" (":inward" / ":outward") wilt verwijderen?', + 'permDeleteWarning' => 'Dingen verwijderen uit Firefly III is permanent en kan niet ongedaan gemaakt worden.', + 'mass_make_selection' => 'Je kan items alsnog redden van de ondergang door het vinkje weg te halen.', + 'delete_all_permanently' => 'Verwijder geselecteerde items permanent', + 'update_all_journals' => 'Wijzig deze transacties', + 'also_delete_transactions' => 'Ook de enige transactie verbonden aan deze rekening wordt verwijderd.|Ook alle :count transacties verbonden aan deze rekening worden verwijderd.', + 'also_delete_connections' => 'De enige transactie gelinkt met dit linktype zal deze verbinding verliezen. | Alle :count transacties met dit linktype zullen deze verbinding verliezen.', + 'also_delete_rules' => 'De enige regel in deze regelgroep wordt ook verwijderd.|Alle :count regels in deze regelgroep worden ook verwijderd.', + 'also_delete_piggyBanks' => 'Ook het spaarpotje verbonden aan deze rekening wordt verwijderd.|Ook alle :count spaarpotjes verbonden aan deze rekening worden verwijderd.', + 'bill_keep_transactions' => 'De enige transactie verbonden aan dit contract blijft bewaard.|De :count transacties verbonden aan dit contract blijven bewaard.', + 'budget_keep_transactions' => 'De enige transactie verbonden aan dit budget blijft bewaard.|De :count transacties verbonden aan dit budget blijven bewaard.', + 'category_keep_transactions' => 'De enige transactie verbonden aan deze categorie blijft bewaard.|De :count transacties verbonden aan deze categorie blijven bewaard.', + 'recurring_keep_transactions' => 'De enige transactie verbonden aan deze periode transactie blijft bewaard.|De :count transacties verbonden aan deze periode transactie blijven bewaard.', + 'tag_keep_transactions' => 'De enige transactie verbonden aan deze tag blijft bewaard.|De :count transacties verbonden aan deze tag blijven bewaard.', + 'check_for_updates' => 'Op updates controleren', 'email' => 'E-mailadres', 'password' => 'Wachtwoord', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Landcode', 'provider_code' => 'Bank of gegevensprovider', - 'due_date' => 'Vervaldatum', - 'payment_date' => 'Betalingsdatum', - 'invoice_date' => 'Factuurdatum', - 'internal_reference' => 'Interne verwijzing', - 'inward' => 'Binnenwaartse beschrijving', - 'outward' => 'Buitenwaartse beschrijving', - 'rule_group_id' => 'Regelgroep', + 'due_date' => 'Vervaldatum', + 'payment_date' => 'Betalingsdatum', + 'invoice_date' => 'Factuurdatum', + 'internal_reference' => 'Interne verwijzing', + 'inward' => 'Binnenwaartse beschrijving', + 'outward' => 'Buitenwaartse beschrijving', + 'rule_group_id' => 'Regelgroep', + 'transaction_description' => 'Transactiebeschrijving', + 'first_date' => 'Eerste datum', + 'transaction_type' => 'Transactietype', + 'repeat_until' => 'Herhalen tot', + 'recurring_description' => 'Beschrijving van de periodieke transactie', + 'repetition_type' => 'Type herhaling', + 'foreign_currency_id' => 'Vreemde valuta', + 'repetition_end' => 'Stopt met herhalen', + 'repetitions' => 'Herhalingen', + 'calendar' => 'Kalender', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/nl_NL/import.php b/resources/lang/nl_NL/import.php index 89381f4bf5..b68492eeaf 100644 --- a/resources/lang/nl_NL/import.php +++ b/resources/lang/nl_NL/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'Je hebt geen rekeningen geselecteerd om uit te importeren.', 'imported_from_account' => 'Geïmporteerd uit ":account"', 'spectre_account_with_number' => 'Rekening :number', + 'job_config_spectre_apply_rules' => 'Regels toepassen', + 'job_config_spectre_apply_rules_text' => 'Standaard worden je regels toegepast op de transacties die je tijdens deze routine importeert. Als je wilt dat dat niet gebeurt, zet dan het vinkje uit.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq rekeningen', 'job_config_bunq_accounts_text' => 'Deze rekeningen zijn geassocieerd met je bunq-account. Kies de rekeningen waar je van wilt importeren, en geef aan waar de gegevens geïmporteerd moeten worden.', 'bunq_no_mapping' => 'Je hebt geen rekeningen geselecteerd om uit te importeren.', 'should_download_config' => 'Download het configuratiebestand voor deze import. Dit maakt toekomstige imports veel eenvoudiger.', 'share_config_file' => 'Als je gegevens hebt geimporteerd van een gewone bank, deel dan je configuratiebestand zodat het makkelijk is voor andere gebruikers om hun gegevens te importeren. Als je je bestand deelt deel je natuurlijk géén privé-gegevens.', - + 'job_config_bunq_apply_rules' => 'Regels toepassen', + 'job_config_bunq_apply_rules_text' => 'Standaard worden je regels toegepast op de transacties die je tijdens deze routine importeert. Als je wilt dat dat niet gebeurt, zet dan het vinkje uit.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/nl_NL/list.php b/resources/lang/nl_NL/list.php index cd17c28646..db765f8c4f 100644 --- a/resources/lang/nl_NL/list.php +++ b/resources/lang/nl_NL/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Laatst ingelogd', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq betalings-ID', + 'repetitions' => 'Herhalingen', + 'title' => 'Titel', + 'transaction_s' => 'Transactie(s)', + 'field' => 'Veld', + 'value' => 'Waarde', ]; diff --git a/resources/lang/nl_NL/validation.php b/resources/lang/nl_NL/validation.php index d5d0537e2b..add99c5012 100644 --- a/resources/lang/nl_NL/validation.php +++ b/resources/lang/nl_NL/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Dit is niet een geldige IBAN.', - 'source_equals_destination' => 'De bronrekening is gelijk aan de doelrekening', + 'source_equals_destination' => 'De bronrekening is gelijk aan de doelrekening.', 'unique_account_number_for_user' => 'Het lijkt erop dat dit rekeningnummer al in gebruik is.', 'unique_iban_for_user' => 'Het lijkt erop dat deze IBAN al in gebruik is.', 'deleted_user' => 'Je kan je niet registreren met dit e-mailadres.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Bestand met naam ":name" is met succes geuploaded.', 'must_exist' => 'Het ID in veld :attribute bestaat niet.', 'all_accounts_equal' => 'Alle rekeningen in dit veld moeten gelijk zijn.', - 'invalid_selection' => 'Ongeldige selectie', + 'invalid_selection' => 'Ongeldige selectie.', 'belongs_user' => 'Deze waarde is ongeldig voor dit veld.', 'at_least_one_transaction' => 'Er is op zijn minst één transactie nodig.', + 'at_least_one_repetition' => 'Er is op zijn minst één herhaling nodig.', + 'require_repeat_until' => 'Je moet een aantal herhalingen opgeven, of een einddatum (repeat_until). Niet beide.', 'require_currency_info' => 'De inhoud van dit veld is ongeldig zonder valutagegevens.', 'equal_description' => 'Transactiebeschrijving mag niet gelijk zijn aan globale beschrijving.', 'file_invalid_mime' => 'Bestand ":name" is van het type ":mime", en die kan je niet uploaden.', 'file_too_large' => 'Bestand ":name" is te groot.', - 'belongs_to_user' => 'De waarde van :attribute is onbekend', + 'belongs_to_user' => 'De waarde van :attribute is onbekend.', 'accepted' => ':attribute moet geaccepteerd zijn.', 'bic' => 'Dit is geen geldige BIC.', + 'at_least_one_trigger' => 'De regel moet minstens één trigger hebben.', + 'at_least_one_action' => 'De regel moet minstens één actie hebben.', + 'base64' => 'Dit is geen geldige base64 gecodeerde data.', + 'model_id_invalid' => 'Dit ID past niet bij dit object.', 'more' => ':attribute moet groter zijn dan nul.', 'active_url' => ':attribute is geen geldige URL.', 'after' => ':attribute moet een datum na :date zijn.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute moet geselecteerde elementen bevatten.', 'unique_for_user' => 'Er is al een entry met deze :attribute.', 'before' => ':attribute moet een datum voor :date zijn.', - 'unique_object_for_user' => 'Deze naam is al in gebruik', - 'unique_account_for_user' => 'This rekeningnaam is already in use', + 'unique_object_for_user' => 'Deze naam is al in gebruik.', + 'unique_account_for_user' => 'Deze rekeningnaam is al in gebruik.', 'between.numeric' => ':attribute moet tussen :min en :max zijn.', 'between.file' => ':attribute moet tussen :min en :max kilobytes zijn.', 'between.string' => ':attribute moet tussen :min en :max karakters zijn.', @@ -85,6 +91,9 @@ return [ 'min.array' => ':attribute moet minimaal :min items bevatten.', 'not_in' => 'Het formaat van :attribute is ongeldig.', 'numeric' => ':attribute moet een nummer zijn.', + 'numeric_native' => 'Het originele bedrag moet een getal zijn.', + 'numeric_destination' => 'Het doelbedrag moet een getal zijn.', + 'numeric_source' => 'Het bronbedrag moet een getal zijn.', 'regex' => ':attribute formaat is ongeldig.', 'required' => ':attribute is verplicht.', 'required_if' => ':attribute is verplicht indien :other gelijk is aan :value.', @@ -109,9 +118,12 @@ return [ 'file' => ':attribute moet een bestand zijn.', 'in_array' => 'Het :attribute veld bestaat niet in :other.', 'present' => 'Het :attribute veld moet aanwezig zijn.', - 'amount_zero' => 'Het totaalbedrag kan niet nul zijn', + 'amount_zero' => 'Het totaalbedrag kan niet nul zijn.', 'unique_piggy_bank_for_user' => 'De naam van de spaarpot moet uniek zijn.', - 'secure_password' => 'Dit is geen sterk wachtwoord. Probeer het nog een keer. Zie ook: http://bit.ly/FF3-password-security', + 'secure_password' => 'Dit is geen sterk wachtwoord. Probeer het nog een keer. Zie ook: http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Dit is geen geldige herhaling voor periodieke transacties.', + 'valid_recurrence_rep_moment' => 'Ongeldig herhaalmoment voor dit type herhaling.', + 'invalid_account_info' => 'Ongeldige rekeninginformatie.', 'attributes' => [ 'email' => 'e-mailadres', 'description' => 'omschrijving', diff --git a/resources/lang/pl_PL/config.php b/resources/lang/pl_PL/config.php index eedc86c9af..d3fcf79ace 100644 --- a/resources/lang/pl_PL/config.php +++ b/resources/lang/pl_PL/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'pl', - 'locale' => 'pl, Polish, polski, pl_PL, pl_PL.utf8, pl_PL.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y o %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Tydzień %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'D MMMM YYYY', - 'date_time_js' => 'D MMMM YYYY [o] HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Tydzień] w. YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'pl', + 'locale' => 'pl, Polish, polski, pl_PL, pl_PL.utf8, pl_PL.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y o %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Tydzień %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'D MMMM YYYY', + 'date_time_js' => 'D MMMM YYYY [o] HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Tydzień] w. YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Poniedziałek', + 'dow_2' => 'Wtorek', + 'dow_3' => 'Środa', + 'dow_4' => 'Czwartek', + 'dow_5' => 'Piątek', + 'dow_6' => 'Sobota', + 'dow_7' => 'Niedziela', ]; diff --git a/resources/lang/pl_PL/demo.php b/resources/lang/pl_PL/demo.php index b4ca24333f..9c1d4678a3 100644 --- a/resources/lang/pl_PL/demo.php +++ b/resources/lang/pl_PL/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Te wydatki, depozyty i transfery nie są szczególnie pomysłowe. Zostały wygenerowane automatycznie.', 'piggy-banks-index' => 'Jak widać, istnieją trzy skarbonki. Użyj przycisków plus i minus, aby wpłynąć na ilość pieniędzy w każdej skarbonce. Kliknij nazwę skarbonki, aby zobaczyć administrację każdej skarbonki.', 'import-index' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/pl_PL/firefly.php b/resources/lang/pl_PL/firefly.php index c904d80cd6..c23c3c6557 100644 --- a/resources/lang/pl_PL/firefly.php +++ b/resources/lang/pl_PL/firefly.php @@ -180,7 +180,7 @@ return [ 'authorization_request_intro' => ':client prosi o pozwolenie na dostęp do Twojej administracji finansowej. Czy chcesz pozwolić :client na dostęp do tych danych?', 'scopes_will_be_able' => 'Ta aplikacja będzie mogła:', 'button_authorize' => 'Autoryzuj', - 'none_in_select_list' => '(none)', + 'none_in_select_list' => '(żadne)', // check for updates: 'update_check_title' => 'Sprawdź aktualizacje', @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Zeskanuj kod QR za pomocą aplikacji w telefonie, takiej jak Authy lub Google Authenticator i wprowadź wygenerowany kod.', 'pref_two_factor_auth_reset_code' => 'Zresetuj kod weryfikacyjny', 'pref_two_factor_auth_disable_2fa' => 'Wyłącz weryfikację dwuetapową', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Zapisz ustawienia', 'saved_preferences' => 'Preferencje zostały zapisane!', 'preferences_general' => 'Ogólne', @@ -668,7 +669,7 @@ return [ 'bill_will_automatch' => 'Rachunek będzie automatycznie powiązany z pasującymi transakcjami', 'skips_over' => 'pomija', 'bill_store_error' => 'Wystąpił nieoczekiwany błąd podczas zapisywania nowego rachunku. Sprawdź pliki dziennika', - 'list_inactive_rule' => 'inactive rule', + 'list_inactive_rule' => 'nieaktywna reguła', // accounts: 'details_for_asset' => 'Szczegóły konta aktywów ":name"', @@ -820,7 +821,7 @@ return [ 'language' => 'Język', 'new_savings_account' => 'Konto oszczędnościowe :bank_name', 'cash_wallet' => 'Portfel gotówkowy', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', // home page: 'yourAccounts' => 'Twoje konta', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Saldo na końcu okresu', 'splitByAccount' => 'Podziel według konta', 'coveredWithTags' => 'Objęte tagami', - 'leftUnbalanced' => 'Pozostawiono niewyważone', 'leftInBudget' => 'Pozostało w budżecie', 'sumOfSums' => 'Suma sum', 'noCategory' => '(bez kategorii)', @@ -1062,7 +1062,7 @@ return [ 'instance_configuration' => 'Konfiguracja', 'firefly_instance_configuration' => 'Opcje konfiguracji dla Firefly III', 'setting_single_user_mode' => 'Tryb pojedynczego użytkownika', - 'setting_single_user_mode_explain' => 'Domyślnie, Firefly III pozwala na jednego (1) użytkownika: Ciebie. Jest to środek bezpieczeństwa uniemożliwiający innym używanie Twojej instalacji, chyba że im pozwolisz. Kolejne rejestracje są zablokowane. Jeżeli odznaczysz to pole, inne osoby będą mogły używać Twojej instalacji Firefly III (zakładając, że jest ona dostępna w Internecie).', + '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 well, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Zapisz konfigurację', 'single_user_administration' => 'Administracja użytkownika dla :email', 'edit_user' => 'Modyfikuj użytkownika :email', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => 'jest (częściowo) zwracane przez', 'is (partially) paid for by_inward' => 'jest (częściowo) opłacane przez', 'is (partially) reimbursed by_inward' => 'jest (częściowo) refundowany przez', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'odnosi się do', '(partially) refunds_outward' => '(częściowo) refundowany', '(partially) pays for_outward' => '(częściowo) płaci za', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Nie można przekonwertować podzielonej transakcji', // Import page (general strings only) - 'import_index_title' => 'Importuj dane do Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Importuj dane', + 'import_transactions' => 'Importuj transakcje', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Ta funkcja nie jest dostępna, gdy używasz Firefly III w środowisku Sandstorm.io.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Nie masz jeszcze żadnych rachunków. Można tworzyć rachunki, aby śledzić regularne wydatki, takie jak czynsz czy ubezpieczenie.', 'no_bills_imperative_default' => 'Czy masz takie regularne rachunki? Utwórz rachunek i śledź swoje płatności:', 'no_bills_create_default' => 'Utwórz rachunek', + + // recurring transactions + 'recurrences' => 'Cykliczne transakcje', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Utwórz cykliczną transakcję', + 'make_new_recurring' => 'Utwórz cykliczną transakcję', + 'recurring_daily' => 'Codziennie', + 'recurring_weekly' => 'Co tydzień w :weekday', + 'recurring_monthly' => 'Co miesiąc w :dayOfMonth dzień', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Co rok w dniu :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Powiązane transakcje', + 'expected_Withdrawals' => 'Oczekiwane wypłaty', + 'expected_Deposits' => 'Oczekiwane wpłaty', + 'expected_Transfers' => 'Oczekiwane transfery', + 'created_Withdrawals' => 'Utworzone wypłaty', + 'created_Deposits' => 'Utworzone wpłaty', + 'created_Transfers' => 'Utworzone transfery', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tagi', + 'recurring_meta_field_notes' => 'Notatki', + 'recurring_meta_field_bill_id' => 'Rachunek', + 'recurring_meta_field_piggy_bank_id' => 'Skarbonka', + 'create_new_recurrence' => 'Utwórz nową cykliczną transakcję', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(brak waluty)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Powtarzaj aż do dnia', + 'repeat_times' => 'Powtarzaj określoną liczbę razy', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/pl_PL/form.php b/resources/lang/pl_PL/form.php index e72c91843f..fe09cafffd 100644 --- a/resources/lang/pl_PL/form.php +++ b/resources/lang/pl_PL/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Nazwa banku', - 'bank_balance' => 'Saldo', - 'savings_balance' => 'Saldo konta oszczędnościowego', - 'credit_card_limit' => 'Limit karty kredytowej', - 'automatch' => 'Dopasuj automatycznie', - 'skip' => 'Pomiń', - 'name' => 'Nazwa', - 'active' => 'Aktywny', - 'amount_min' => 'Minimalna kwota', - 'amount_max' => 'Maksymalna kwota', - 'match' => 'Dopasowanie', - 'strict' => 'Tryb ścisły', - 'repeat_freq' => 'Powtarza się', - 'journal_currency_id' => 'Waluta', - 'currency_id' => 'Waluta', - 'transaction_currency_id' => 'Waluta', - 'external_ip' => 'Zewnętrzny adres IP Twojego serwera', - 'attachments' => 'Załączniki', - 'journal_amount' => 'Kwota', - 'journal_source_account_name' => 'Konto przychodów (źródło)', - 'journal_source_account_id' => 'Konto aktywów (źródło)', - 'BIC' => 'BIC', - 'verify_password' => 'Sprawdź bezpieczeństwo hasła', - 'source_account' => 'Konto źródłowe', - 'destination_account' => 'Konto docelowe', - 'journal_destination_account_id' => 'Konto aktywów (przeznaczenie)', - 'asset_destination_account' => 'Konto aktywów (przeznaczenie)', - 'asset_source_account' => 'Konto aktywów (źródło)', - 'journal_description' => 'Opis', - 'note' => 'Notatki', - 'split_journal' => 'Podziel tę transakcję', - 'split_journal_explanation' => 'Podziel transakcję na wiele części', - 'currency' => 'Waluta', - 'account_id' => 'Konto aktywów', - 'budget_id' => 'Budżet', - 'openingBalance' => 'Bilans otwarcia', - 'tagMode' => 'Tryb tagów', - 'tag_position' => 'Lokalizacja taga', - 'virtualBalance' => 'Wirtualne saldo', - 'targetamount' => 'Kwota docelowa', - 'accountRole' => 'Rola konta', - 'openingBalanceDate' => 'Data salda otwarcia', - 'ccType' => 'Plan płatności kartą kredytową', - 'ccMonthlyPaymentDate' => 'Miesięczny termin spłaty karty kredytowej', - 'piggy_bank_id' => 'Skarbonka', - 'returnHere' => 'Wróć tutaj', - 'returnHereExplanation' => 'Po zapisaniu, wrócić tutaj.', - 'returnHereUpdateExplanation' => 'Po aktualizacji, wróć tutaj.', - 'description' => 'Opis', - 'expense_account' => 'Konto wydatków', - 'revenue_account' => 'Konto przychodów', - 'decimal_places' => 'Miejsca dziesiętne', - 'exchange_rate_instruction' => 'Zagraniczne waluty', - 'source_amount' => 'Kwota (źródło)', - 'destination_amount' => 'Kwota (przeznaczenie)', - 'native_amount' => 'Źródłowa kwota', - 'new_email_address' => 'Nowy adres e-mail', - 'verification' => 'Weryfikacja', - 'api_key' => 'Klucz API', - 'remember_me' => 'Zapamiętaj mnie', + 'bank_name' => 'Nazwa banku', + 'bank_balance' => 'Saldo', + 'savings_balance' => 'Saldo konta oszczędnościowego', + 'credit_card_limit' => 'Limit karty kredytowej', + 'automatch' => 'Dopasuj automatycznie', + 'skip' => 'Pomiń', + 'name' => 'Nazwa', + 'active' => 'Aktywny', + 'amount_min' => 'Minimalna kwota', + 'amount_max' => 'Maksymalna kwota', + 'match' => 'Dopasowanie', + 'strict' => 'Tryb ścisły', + 'repeat_freq' => 'Powtarza się', + 'journal_currency_id' => 'Waluta', + 'currency_id' => 'Waluta', + 'transaction_currency_id' => 'Waluta', + 'external_ip' => 'Zewnętrzny adres IP Twojego serwera', + 'attachments' => 'Załączniki', + 'journal_amount' => 'Kwota', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Sprawdź bezpieczeństwo hasła', + 'source_account' => 'Konto źródłowe', + 'destination_account' => 'Konto docelowe', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Konto aktywów (przeznaczenie)', + 'asset_source_account' => 'Konto aktywów (źródło)', + 'journal_description' => 'Opis', + 'note' => 'Notatki', + 'split_journal' => 'Podziel tę transakcję', + 'split_journal_explanation' => 'Podziel transakcję na wiele części', + 'currency' => 'Waluta', + 'account_id' => 'Konto aktywów', + 'budget_id' => 'Budżet', + 'openingBalance' => 'Bilans otwarcia', + 'tagMode' => 'Tryb tagów', + 'tag_position' => 'Lokalizacja taga', + 'virtualBalance' => 'Wirtualne saldo', + 'targetamount' => 'Kwota docelowa', + 'accountRole' => 'Rola konta', + 'openingBalanceDate' => 'Data salda otwarcia', + 'ccType' => 'Plan płatności kartą kredytową', + 'ccMonthlyPaymentDate' => 'Miesięczny termin spłaty karty kredytowej', + 'piggy_bank_id' => 'Skarbonka', + 'returnHere' => 'Wróć tutaj', + 'returnHereExplanation' => 'Po zapisaniu, wrócić tutaj.', + 'returnHereUpdateExplanation' => 'Po aktualizacji, wróć tutaj.', + 'description' => 'Opis', + 'expense_account' => 'Konto wydatków', + 'revenue_account' => 'Konto przychodów', + 'decimal_places' => 'Miejsca dziesiętne', + 'exchange_rate_instruction' => 'Zagraniczne waluty', + 'source_amount' => 'Kwota (źródło)', + 'destination_amount' => 'Kwota (przeznaczenie)', + 'native_amount' => 'Źródłowa kwota', + 'new_email_address' => 'Nowy adres e-mail', + 'verification' => 'Weryfikacja', + 'api_key' => 'Klucz API', + 'remember_me' => 'Zapamiętaj mnie', 'source_account_asset' => 'Konto źródłowe (konto aktywów)', 'destination_account_expense' => 'Konto docelowe (konto wydatków)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Konwertuj wpłatę', 'convert_Transfer' => 'Konwertuj transfer', - 'amount' => 'Kwota', - 'foreign_amount' => 'Kwota zagraniczna', - 'existing_attachments' => 'Istniejące załączniki', - 'date' => 'Data', - 'interest_date' => 'Data odsetek', - 'book_date' => 'Data księgowania', - 'process_date' => 'Data przetworzenia', - 'category' => 'Kategoria', - 'tags' => 'Tagi', - 'deletePermanently' => 'Usuń trwale', - 'cancel' => 'Anuluj', - 'targetdate' => 'Data docelowa', - 'startdate' => 'Data rozpoczęcia', - 'tag' => 'Tag', - 'under' => 'Poniżej', - 'symbol' => 'Symbol', - 'code' => 'Kod', - 'iban' => 'IBAN', - 'accountNumber' => 'Numer konta', - 'creditCardNumber' => 'Numer karty kredytowej', - 'has_headers' => 'Nagłówki', - 'date_format' => 'Format daty', - 'specifix' => 'Poprawki dla banku lub pliku', - 'attachments[]' => 'Załączniki', - 'store_new_withdrawal' => 'Zapisz nową wypłatę', - 'store_new_deposit' => 'Zapisz nową wpłatę', - 'store_new_transfer' => 'Zapisz nowy transfer', - 'add_new_withdrawal' => 'Dodaj nową wypłatę', - 'add_new_deposit' => 'Dodaj nową wpłatę', - 'add_new_transfer' => 'Dodaj nowy transfer', - 'title' => 'Tytuł', - 'notes' => 'Notatki', - 'filename' => 'Nazwa pliku', - 'mime' => 'Typ MIME', - 'size' => 'Rozmiar', - 'trigger' => 'Wyzwalacz', - 'stop_processing' => 'Zatrzymaj przetwarzanie', - 'start_date' => 'Początek zakresu', - 'end_date' => 'Koniec zakresu', - 'export_start_range' => 'Początek okresu eksportu', - 'export_end_range' => 'Koniec okresu eksportu', - 'export_format' => 'Format pliku', - 'include_attachments' => 'Uwzględnij dołączone załączniki', - 'include_old_uploads' => 'Dołącz zaimportowane dane', - 'accounts' => 'Eksportuj transakcje z tych kont', - 'delete_account' => 'Usuń konto ":name"', - 'delete_bill' => 'Usuń rachunek ":name"', - 'delete_budget' => 'Usuń budżet ":name"', - 'delete_category' => 'Usuń kategorię ":name"', - 'delete_currency' => 'Usuń walutę ":name"', - 'delete_journal' => 'Usuń transakcję z opisem ":description"', - 'delete_attachment' => 'Usuń załącznik ":name"', - 'delete_rule' => 'Usuń regułę ":title"', - 'delete_rule_group' => 'Usuń grupę reguł ":title"', - 'delete_link_type' => 'Usuń typ łącza ":name"', - 'delete_user' => 'Usuń użytkownika ":email"', - 'user_areYouSure' => 'Jeśli usuniesz użytkownika ":email", wszystko zniknie. Nie ma cofania, przywracania ani czegokolwiek. Jeśli usuniesz siebie, stracisz dostęp do tej instalacji Firefly III.', - 'attachment_areYouSure' => 'Czy na pewno chcesz usunąć załącznik o nazwie ":name"?', - 'account_areYouSure' => 'Czy na pewno chcesz usunąć konto o nazwie ":name"?', - 'bill_areYouSure' => 'Czy na pewno chcesz usunąć rachunek o nazwie ":name"?', - 'rule_areYouSure' => 'Czy na pewno chcesz usunąć regułę o nazwie ":name"?', - 'ruleGroup_areYouSure' => 'Czy na pewno chcesz usunąć grupę reguł o nazwie ":name"?', - 'budget_areYouSure' => 'Czy na pewno chcesz usunąć budżet o nazwie ":name"?', - 'category_areYouSure' => 'Czy na pewno chcesz usunąć kategorię o nazwie ":name"?', - 'currency_areYouSure' => 'Czy na pewno chcesz usunąć walutę o nazwie ":name"?', - 'piggyBank_areYouSure' => 'Czy na pewno chcesz usunąć skarbonkę o nazwie ":name"?', - 'journal_areYouSure' => 'Czy na pewno chcesz usunąć transakcję opisaną ":description"?', - 'mass_journal_are_you_sure' => 'Czy na pewno chcesz usunąć te transakcje?', - 'tag_areYouSure' => 'Czy na pewno chcesz usunąć tag ":tag"?', - 'journal_link_areYouSure' => 'Czy na pewno chcesz usunąć powiązanie między :source a :destination?', - 'linkType_areYouSure' => 'Czy na pewno chcesz usunąć typ łącza ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Usuwanie rzeczy z Firefly III jest trwałe i nie można tego cofnąć.', - 'mass_make_selection' => 'Nadal możesz zapobiec usunięciu elementów, odznaczając je.', - 'delete_all_permanently' => 'Trwale usuń zaznaczone', - 'update_all_journals' => 'Zmodyfikuj te transakcje', - 'also_delete_transactions' => 'Jedyna transakcja powiązana z tym kontem zostanie również usunięta.|Wszystkie transakcje (:count) powiązane z tym kontem zostaną również usunięta.', - 'also_delete_connections' => 'Jedyna transakcja połączona z tym typem łącza utraci to połączenie.|Wszystkie transakcje (:count) połączone tym typem łącza utracą swoje połączenie.', - 'also_delete_rules' => 'Jedyna reguła połączona z tą grupą reguł zostanie również usunięta.|Wszystkie reguły (:count) połączone tą grupą reguł zostaną również usunięte.', - 'also_delete_piggyBanks' => 'Jedyna skarbonka połączona z tym kontem zostanie również usunięta.|Wszystkie skarbonki (:count) połączone z tym kontem zostaną również usunięte.', - 'bill_keep_transactions' => 'Jedyna transakcja związana z tym rachunkiem nie zostanie usunięta. | Wszystkie :count transakcje związane z tym rachunkiem zostaną oszczędzone.', - 'budget_keep_transactions' => 'Jedyna transakcja związana z tym budżetem nie zostanie usunięta.|Wszystkie transakcje (:count) związane z tym budżetem zostaną oszczędzone.', - 'category_keep_transactions' => 'Jedyna transakcja związana z tą kategorią nie zostanie usunięta.|Wszystkie transakcje (:count) związane z tą kategorią zostaną oszczędzone.', - 'tag_keep_transactions' => 'Jedyna transakcja związana z tym tagiem nie zostanie usunięta.|Wszystkie transakcje (:count) związane z tym tagiem zostaną oszczędzone.', - 'check_for_updates' => 'Sprawdź aktualizacje', + 'amount' => 'Kwota', + 'foreign_amount' => 'Kwota zagraniczna', + 'existing_attachments' => 'Istniejące załączniki', + 'date' => 'Data', + 'interest_date' => 'Data odsetek', + 'book_date' => 'Data księgowania', + 'process_date' => 'Data przetworzenia', + 'category' => 'Kategoria', + 'tags' => 'Tagi', + 'deletePermanently' => 'Usuń trwale', + 'cancel' => 'Anuluj', + 'targetdate' => 'Data docelowa', + 'startdate' => 'Data rozpoczęcia', + 'tag' => 'Tag', + 'under' => 'Poniżej', + 'symbol' => 'Symbol', + 'code' => 'Kod', + 'iban' => 'IBAN', + 'accountNumber' => 'Numer konta', + 'creditCardNumber' => 'Numer karty kredytowej', + 'has_headers' => 'Nagłówki', + 'date_format' => 'Format daty', + 'specifix' => 'Poprawki dla banku lub pliku', + 'attachments[]' => 'Załączniki', + 'store_new_withdrawal' => 'Zapisz nową wypłatę', + 'store_new_deposit' => 'Zapisz nową wpłatę', + 'store_new_transfer' => 'Zapisz nowy transfer', + 'add_new_withdrawal' => 'Dodaj nową wypłatę', + 'add_new_deposit' => 'Dodaj nową wpłatę', + 'add_new_transfer' => 'Dodaj nowy transfer', + 'title' => 'Tytuł', + 'notes' => 'Notatki', + 'filename' => 'Nazwa pliku', + 'mime' => 'Typ MIME', + 'size' => 'Rozmiar', + 'trigger' => 'Wyzwalacz', + 'stop_processing' => 'Zatrzymaj przetwarzanie', + 'start_date' => 'Początek zakresu', + 'end_date' => 'Koniec zakresu', + 'export_start_range' => 'Początek okresu eksportu', + 'export_end_range' => 'Koniec okresu eksportu', + 'export_format' => 'Format pliku', + 'include_attachments' => 'Uwzględnij dołączone załączniki', + 'include_old_uploads' => 'Dołącz zaimportowane dane', + 'accounts' => 'Eksportuj transakcje z tych kont', + 'delete_account' => 'Usuń konto ":name"', + 'delete_bill' => 'Usuń rachunek ":name"', + 'delete_budget' => 'Usuń budżet ":name"', + 'delete_category' => 'Usuń kategorię ":name"', + 'delete_currency' => 'Usuń walutę ":name"', + 'delete_journal' => 'Usuń transakcję z opisem ":description"', + 'delete_attachment' => 'Usuń załącznik ":name"', + 'delete_rule' => 'Usuń regułę ":title"', + 'delete_rule_group' => 'Usuń grupę reguł ":title"', + 'delete_link_type' => 'Usuń typ łącza ":name"', + 'delete_user' => 'Usuń użytkownika ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Jeśli usuniesz użytkownika ":email", wszystko zniknie. Nie ma cofania, przywracania ani czegokolwiek. Jeśli usuniesz siebie, stracisz dostęp do tej instalacji Firefly III.', + 'attachment_areYouSure' => 'Czy na pewno chcesz usunąć załącznik o nazwie ":name"?', + 'account_areYouSure' => 'Czy na pewno chcesz usunąć konto o nazwie ":name"?', + 'bill_areYouSure' => 'Czy na pewno chcesz usunąć rachunek o nazwie ":name"?', + 'rule_areYouSure' => 'Czy na pewno chcesz usunąć regułę o nazwie ":name"?', + 'ruleGroup_areYouSure' => 'Czy na pewno chcesz usunąć grupę reguł o nazwie ":name"?', + 'budget_areYouSure' => 'Czy na pewno chcesz usunąć budżet o nazwie ":name"?', + 'category_areYouSure' => 'Czy na pewno chcesz usunąć kategorię o nazwie ":name"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => 'Czy na pewno chcesz usunąć walutę o nazwie ":name"?', + 'piggyBank_areYouSure' => 'Czy na pewno chcesz usunąć skarbonkę o nazwie ":name"?', + 'journal_areYouSure' => 'Czy na pewno chcesz usunąć transakcję opisaną ":description"?', + 'mass_journal_are_you_sure' => 'Czy na pewno chcesz usunąć te transakcje?', + 'tag_areYouSure' => 'Czy na pewno chcesz usunąć tag ":tag"?', + 'journal_link_areYouSure' => 'Czy na pewno chcesz usunąć powiązanie między :source a :destination?', + 'linkType_areYouSure' => 'Czy na pewno chcesz usunąć typ łącza ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Usuwanie rzeczy z Firefly III jest trwałe i nie można tego cofnąć.', + 'mass_make_selection' => 'Nadal możesz zapobiec usunięciu elementów, odznaczając je.', + 'delete_all_permanently' => 'Trwale usuń zaznaczone', + 'update_all_journals' => 'Zmodyfikuj te transakcje', + 'also_delete_transactions' => 'Jedyna transakcja powiązana z tym kontem zostanie również usunięta.|Wszystkie transakcje (:count) powiązane z tym kontem zostaną również usunięta.', + 'also_delete_connections' => 'Jedyna transakcja połączona z tym typem łącza utraci to połączenie.|Wszystkie transakcje (:count) połączone tym typem łącza utracą swoje połączenie.', + 'also_delete_rules' => 'Jedyna reguła połączona z tą grupą reguł zostanie również usunięta.|Wszystkie reguły (:count) połączone tą grupą reguł zostaną również usunięte.', + 'also_delete_piggyBanks' => 'Jedyna skarbonka połączona z tym kontem zostanie również usunięta.|Wszystkie skarbonki (:count) połączone z tym kontem zostaną również usunięte.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Sprawdź aktualizacje', 'email' => 'Adres email', 'password' => 'Hasło', @@ -186,10 +189,10 @@ return [ 'blocked_code' => 'Powód blokady', // import - 'apply_rules' => 'Apply rules', - 'artist' => 'Artist', + 'apply_rules' => 'Zastosuj reguły', + 'artist' => 'Artysta', 'album' => 'Album', - 'song' => 'Song', + 'song' => 'Piosenka', // admin @@ -216,11 +219,23 @@ return [ 'country_code' => 'Kod kraju', 'provider_code' => 'Dostawca banku lub danych', - 'due_date' => 'Termin realizacji', - 'payment_date' => 'Data płatności', - 'invoice_date' => 'Data faktury', - 'internal_reference' => 'Wewnętrzny numer', - 'inward' => 'Opis wewnętrzny', - 'outward' => 'Opis zewnętrzny', - 'rule_group_id' => 'Grupa reguł', + 'due_date' => 'Termin realizacji', + 'payment_date' => 'Data płatności', + 'invoice_date' => 'Data faktury', + 'internal_reference' => 'Wewnętrzny numer', + 'inward' => 'Opis wewnętrzny', + 'outward' => 'Opis zewnętrzny', + 'rule_group_id' => 'Grupa reguł', + 'transaction_description' => 'Opis transakcji', + 'first_date' => 'Data początkowa', + 'transaction_type' => 'Typ transakcji', + 'repeat_until' => 'Powtarzaj aż', + 'recurring_description' => 'Opis cyklicznej transakcji', + 'repetition_type' => 'Typ powtórzeń', + 'foreign_currency_id' => 'Zagraniczna waluta', + 'repetition_end' => 'Koniec powtórzeń', + 'repetitions' => 'Powtórzenia', + 'calendar' => 'Kalendarz', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/pl_PL/import.php b/resources/lang/pl_PL/import.php index 6b2aa6c068..8e2b109e85 100644 --- a/resources/lang/pl_PL/import.php +++ b/resources/lang/pl_PL/import.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ // ALL breadcrumbs and subtitles: - 'index_breadcrumb' => 'Import data into Firefly III', + 'index_breadcrumb' => 'Importuj dane do Firefly III', 'prerequisites_breadcrumb_fake' => 'Prerequisites for the fake import provider', 'prerequisites_breadcrumb_spectre' => 'Prerequisites for Spectre', 'prerequisites_breadcrumb_bunq' => 'Prerequisites for bunq', @@ -37,17 +37,17 @@ return [ 'general_index_intro' => 'Welcome to Firefly III\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', // import provider strings (index): 'button_fake' => 'Fake an import', - 'button_file' => 'Import a file', - 'button_bunq' => 'Import from bunq', - 'button_spectre' => 'Import using Spectre', - 'button_plaid' => 'Import using Plaid', - 'button_yodlee' => 'Import using Yodlee', - 'button_quovo' => 'Import using Quovo', + 'button_file' => 'Importuj plik', + 'button_bunq' => 'Importuj z bunq', + 'button_spectre' => 'Importuj za pomocą Spectre', + 'button_plaid' => 'Importuj za pomocą Plaid', + 'button_yodlee' => 'Importuj za pomocą Yodlee', + 'button_quovo' => 'Importuj za pomocą Quovo', // global config box (index) - 'global_config_title' => 'Global import configuration', + 'global_config_title' => 'Globalna konfiguracja importu', 'global_config_text' => 'In the future, this box will feature preferences that apply to ALL import providers above.', // prerequisites box (index) - 'need_prereq_title' => 'Import prerequisites', + 'need_prereq_title' => 'Wymagania importu', 'need_prereq_intro' => 'Some import methods need your attention before they can be used. For example, they might require special API keys or application secrets. You can configure them here. The icon indicates if these prerequisites have been met.', 'do_prereq_fake' => 'Prerequisites for the fake provider', 'do_prereq_file' => 'Prerequisites for file imports', @@ -57,7 +57,7 @@ return [ 'do_prereq_yodlee' => 'Prerequisites for imports using Yodlee', 'do_prereq_quovo' => 'Prerequisites for imports using Quovo', // provider config box (index) - 'can_config_title' => 'Import configuration', + 'can_config_title' => 'Importuj konfigurację', 'can_config_intro' => 'Some import methods can be configured to your liking. They have extra settings you can tweak.', 'do_config_fake' => 'Configuration for the fake provider', 'do_config_file' => 'Configuration for file imports', @@ -107,48 +107,51 @@ return [ 'job_config_uc_date_help' => 'Date time format in your file. Follow the format as this page indicates. The default value will parse dates that look like this: :dateExample.', 'job_config_uc_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'job_config_uc_account_help' => 'If your file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the file belong to.', - 'job_config_uc_apply_rules_title' => 'Apply rules', + 'job_config_uc_apply_rules_title' => 'Zastosuj reguły', 'job_config_uc_apply_rules_text' => 'Applies your rules to every imported transaction. Note that this slows the import significantly.', 'job_config_uc_specifics_title' => 'Bank-specific options', 'job_config_uc_specifics_txt' => 'Some banks deliver badly formatted files. Firefly III can fix those automatically. If your bank delivers such files but it\'s not listed here, please open an issue on GitHub.', - 'job_config_uc_submit' => 'Continue', + 'job_config_uc_submit' => 'Kontynuuj', 'invalid_import_account' => 'You have selected an invalid account to import into.', // job configuration for Spectre: - 'job_config_spectre_login_title' => 'Choose your login', + 'job_config_spectre_login_title' => 'Wybierz swój login', 'job_config_spectre_login_text' => 'Firefly III has found :count existing login(s) in your Spectre account. Which one would you like to use to import from?', - 'spectre_login_status_active' => 'Active', - 'spectre_login_status_inactive' => 'Inactive', - 'spectre_login_status_disabled' => 'Disabled', + 'spectre_login_status_active' => 'Aktywny', + 'spectre_login_status_inactive' => 'Nieaktywny', + 'spectre_login_status_disabled' => 'Wyłączony', 'spectre_login_new_login' => 'Login with another bank, or one of these banks with different credentials.', 'job_config_spectre_accounts_title' => 'Select accounts to import from', 'job_config_spectre_accounts_text' => 'You have selected ":name" (:country). You have :count account(s) available from this provider. Please select the Firefly III asset account(s) where the transactions from these accounts should be stored. Remember, in order to import data both the Firefly III account and the ":name"-account must have the same currency.', 'spectre_no_supported_accounts' => 'You cannot import from this account due to a currency mismatch.', - 'spectre_do_not_import' => '(do not import)', - 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', - 'imported_from_account' => 'Imported from ":account"', - 'spectre_account_with_number' => 'Account :number', + 'spectre_do_not_import' => '(nie importuj)', + 'spectre_no_mapping' => 'Wygląda na to, że nie wybrałeś żadnych kont z których można zaimportować dane.', + 'imported_from_account' => 'Zaimportowane z ":account"', + 'spectre_account_with_number' => ':number konta', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: - 'job_config_bunq_accounts_title' => 'bunq accounts', + 'job_config_bunq_accounts_title' => 'konta bunq', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', 'spectre_extra_key_status' => 'Status', - 'spectre_extra_key_card_type' => 'Card type', - 'spectre_extra_key_account_name' => 'Account name', - 'spectre_extra_key_client_name' => 'Client name', - 'spectre_extra_key_account_number' => 'Account number', - 'spectre_extra_key_blocked_amount' => 'Blocked amount', - 'spectre_extra_key_available_amount' => 'Available amount', - 'spectre_extra_key_credit_limit' => 'Credit limit', - 'spectre_extra_key_interest_rate' => 'Interest rate', + 'spectre_extra_key_card_type' => 'Typ karty', + 'spectre_extra_key_account_name' => 'Nazwa konta', + 'spectre_extra_key_client_name' => 'Nazwa klienta', + 'spectre_extra_key_account_number' => 'Numer konta', + 'spectre_extra_key_blocked_amount' => 'Zablokowana kwota', + 'spectre_extra_key_available_amount' => 'Dostępna kwota', + 'spectre_extra_key_credit_limit' => 'Limit kredytowy', + 'spectre_extra_key_interest_rate' => 'Oprocentowanie', 'spectre_extra_key_expiry_date' => 'Expiry date', - 'spectre_extra_key_open_date' => 'Open date', - 'spectre_extra_key_current_time' => 'Current time', + 'spectre_extra_key_open_date' => 'Data otwarcia', + 'spectre_extra_key_current_time' => 'Aktualny czas', 'spectre_extra_key_current_date' => 'Current date', 'spectre_extra_key_cards' => 'Cards', 'spectre_extra_key_units' => 'Units', @@ -177,15 +180,15 @@ return [ 'job_config_roles_no_example' => 'No example data available', 'job_config_roles_fa_warning' => 'If you mark a column as containing an amount in a foreign currency, you must also set the column that contains which currency it is.', 'job_config_roles_rwarning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', - 'job_config_roles_colum_count' => 'Column', + 'job_config_roles_colum_count' => 'Kolumna', // job config for the file provider (stage: mapping): 'job_config_map_title' => 'Import setup (4/4) - Connect import data to Firefly III data', 'job_config_map_text' => 'In the following tables, the left value shows you information found in your uploaded 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.', 'job_config_map_nothing' => 'There is no data present in your file that you can map to existing values. Please press "Start the import" to continue.', - 'job_config_field_value' => 'Field value', - 'job_config_field_mapped' => 'Mapped to', + 'job_config_field_value' => 'Wartość pola', + 'job_config_field_mapped' => 'Zmapowane na', 'map_do_not_map' => '(nie mapuj)', - 'job_config_map_submit' => 'Start the import', + 'job_config_map_submit' => 'Rozpocznij import', // import status page: @@ -196,11 +199,11 @@ return [ 'status_job_running' => 'Please wait, running the import...', 'status_job_storing' => 'Please wait, storing data...', 'status_job_rules' => 'Please wait, running rules...', - 'status_fatal_title' => 'Fatal error', + 'status_fatal_title' => 'Błąd krytyczny', 'status_fatal_text' => 'The import has suffered from an error it could not recover from. Apologies!', 'status_fatal_more' => 'This (possibly very cryptic) error message is complemented by log files, which you can find on your hard drive, or in the Docker container where you run Firefly III from.', - 'status_finished_title' => 'Import finished', - 'status_finished_text' => 'The import has finished.', + 'status_finished_title' => 'Import zakończony', + 'status_finished_text' => 'Importowanie zostało zakończone.', 'finished_with_errors' => 'There were some errors during the import. Please review them carefully.', 'unknown_import_result' => 'Unknown import result', 'result_no_transactions' => 'No transactions have been imported. Perhaps they were all duplicates is simply no transactions where present to be imported. Perhaps the log files can tell you what happened. If you import data regularly, this is normal.', diff --git a/resources/lang/pl_PL/list.php b/resources/lang/pl_PL/list.php index a18fe7286e..f2a7bfedff 100644 --- a/resources/lang/pl_PL/list.php +++ b/resources/lang/pl_PL/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Last login', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq payment ID', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transakcja(e)', + 'field' => 'Pole', + 'value' => 'Wartość', ]; diff --git a/resources/lang/pl_PL/validation.php b/resources/lang/pl_PL/validation.php index 0ae9b71c19..2cd06c8b85 100644 --- a/resources/lang/pl_PL/validation.php +++ b/resources/lang/pl_PL/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'To nie jest prawidłowy IBAN.', - 'source_equals_destination' => 'Konto źródłowe jest równe kontu docelowemu', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Wygląda na to, że ten numer konta jest już w użyciu.', 'unique_iban_for_user' => 'Wygląda na to, że ten IBAN jest już w użyciu.', 'deleted_user' => 'Ze względu na zabezpieczenia nie możesz się zarejestrować używając tego adresu e-mail.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Pomyślnie wgrano plik ":name".', 'must_exist' => 'Identyfikator w polu :attribute nie istnieje w bazie danych.', 'all_accounts_equal' => 'Wszystkie konta w tym polu muszą być takie same.', - 'invalid_selection' => 'Twój wybór jest nieprawidłowy', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'Ta wartość jest nieprawidłowa dla tego pola.', 'at_least_one_transaction' => 'Wymaga co najmniej jednej transakcji.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'Treść tego pola jest nieprawidłowa bez informacji o walucie.', 'equal_description' => 'Opis transakcji nie powinien być równy globalnemu opisowi.', 'file_invalid_mime' => 'Plik ":name" jest typu ":mime", który nie jest akceptowany jako nowy plik do przekazania.', 'file_too_large' => 'Plik ":name" jest zbyt duży.', - 'belongs_to_user' => 'Wartość :attribute jest nieznana', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => ':attribute musi zostać zaakceptowany.', 'bic' => 'To nie jest prawidłowy BIC.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute musi być większy od zera.', 'active_url' => ':attribute nie jest prawidłowym adresem URL.', 'after' => ':attribute musi być datą późniejszą od :date.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute musi być tablicą.', 'unique_for_user' => 'Istnieje już wpis z tym :attribute.', 'before' => ':attribute musi być wcześniejszą datą w stosunku do :date.', - 'unique_object_for_user' => 'Ta nazwa jest już w użyciu', - 'unique_account_for_user' => 'Ta nazwa konta jest już w użyciu', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => ':attribute musi się mieścić w zakresie pomiędzy :min a :max.', 'between.file' => ':attribute musi się mieścić w zakresie pomiędzy :min oraz :max kilobajtów.', 'between.string' => ':attribute musi zawierać pomiędzy :min a :max znaków.', @@ -85,6 +91,9 @@ return [ 'min.array' => ':attribute musi zawierać przynajmniej :min elementów.', 'not_in' => 'Wybrany :attribute jest nieprawidłowy.', 'numeric' => ':attribute musi byc liczbą.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'Format :attribute jest nieprawidłowy.', 'required' => 'Pole :attribute jest wymagane.', 'required_if' => 'Pole :attribute jest wymagane gdy :other jest :value.', @@ -109,9 +118,12 @@ return [ 'file' => ':attribute musi być plikiem.', 'in_array' => 'Pole :attribute nie istnieje w :other.', 'present' => 'Pole :attribute musi być obecne.', - 'amount_zero' => 'Całkowita kwota nie może być zerem', + 'amount_zero' => 'The total amount cannot be zero.', 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'adres e-mail', 'description' => 'opis', diff --git a/resources/lang/pt_BR/config.php b/resources/lang/pt_BR/config.php index ce507e8a05..f0401d93b3 100644 --- a/resources/lang/pt_BR/config.php +++ b/resources/lang/pt_BR/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'pt-br', - 'locale' => 'pt-br, pt_BR, pt_BR.utf8, pt_BR.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e de %B de %Y', - 'date_time' => '%B %e, %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Semana %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'pt-br', + 'locale' => 'pt-br, pt_BR, pt_BR.utf8, pt_BR.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e de %B de %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%B %e, %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Semana %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'MMMM Do, YYYY', + 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Monday', + 'dow_2' => 'Tuesday', + 'dow_3' => 'Wednesday', + 'dow_4' => 'Thursday', + 'dow_5' => 'Friday', + 'dow_6' => 'Saturday', + 'dow_7' => 'Sunday', ]; diff --git a/resources/lang/pt_BR/demo.php b/resources/lang/pt_BR/demo.php index b6960bda12..a52519475f 100644 --- a/resources/lang/pt_BR/demo.php +++ b/resources/lang/pt_BR/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Estas despesas, depósitos e transferências não são fantasiosas. Elas foram geradas automaticamente.', 'piggy-banks-index' => 'Como você pode ver, existem três cofrinhos. Use o sinal de mais e menos botões para influenciar a quantidade de dinheiro em cada cofrinho. Clique no nome do cofrinho para ver a administração de cada cofrinho.', 'import-index' => 'Qualquer arquivo CSV pode ser importado para o Firefly III. Importações de dados de bunq e Specter também são suportadas. Outros bancos e agregadores financeiros serão implementados futuramente. Como usuário de demonstração, no entanto, você só pode ver o provedor "falso" em ação. Ele irá gerar transações aleatórias para lhe mostrar como funciona o processo.', + 'recurring-index' => 'Please note that this feature is under active development and may not work as expected.', + 'recurring-create' => 'Please note that this feature is under active development and may not work as expected.', ]; diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 988bf0b85a..41ad6e78ff 100644 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Scaneie o código QR com um aplicativo em seu telefone como Authy ou Google Authenticator e insira o código gerado.', 'pref_two_factor_auth_reset_code' => 'Redefinir o código de verificação', 'pref_two_factor_auth_disable_2fa' => 'Desativar verificação em duas etapas', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Salvar definições', 'saved_preferences' => 'Preferências salvas!', 'preferences_general' => 'Geral', @@ -820,7 +821,7 @@ return [ 'language' => 'Idioma', 'new_savings_account' => 'Conta de poupança :bank_name', 'cash_wallet' => 'Carteira de dinheiro', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', // home page: 'yourAccounts' => 'Suas contas', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Saldo no final do período', 'splitByAccount' => 'Dividir por conta', 'coveredWithTags' => 'Coberto com tags', - 'leftUnbalanced' => 'Deixar desequilibrado', 'leftInBudget' => 'Deixou no orçamento', 'sumOfSums' => 'Soma dos montantes', 'noCategory' => '(sem categoria)', @@ -1062,7 +1062,7 @@ return [ 'instance_configuration' => 'Configuração', 'firefly_instance_configuration' => 'Opções de configuração para Firefly III', 'setting_single_user_mode' => 'Modo de usuário único', - 'setting_single_user_mode_explain' => 'Por padrão, o Firefly III aceita apenas um (1) registro: você. Esta é uma medida de segurança, impedindo que outros usem sua instância, a menos que você permita. Os registros futuros estão bloqueados. Quando você desmarca essa caixa, outros podem usar sua instância, assumindo que podem alcançá-la (quando conectados à Internet).', + '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 well, assuming they can reach it (when it is connected to the internet).', 'store_configuration' => 'Configuração da Loja', 'single_user_administration' => 'Administração de usuários para :email', 'edit_user' => 'Editar usuário :email', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => 'é (parcialmente) devolvido por', 'is (partially) paid for by_inward' => 'é (parcialmente) pago por', 'is (partially) reimbursed by_inward' => 'é (parcialmente) reembolsado por', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'relacionado a', '(partially) refunds_outward' => 'reembolsos (parcialmente)', '(partially) pays for_outward' => 'paga (parcialmente) por', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Não é possível converter uma transação dividida', // Import page (general strings only) - 'import_index_title' => 'Importar dados para o Firefly III', + 'import_index_title' => 'Import transactions into Firefly III', 'import_data' => 'Importar dados', + 'import_transactions' => 'Import transactions', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Esta função não está disponível quando você está usando o Firefly III dentro de um ambiente Sandstorm.io.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'Você ainda não possui faturas. Você pode criar faturas para acompanhar as despesas regulares, como sua renda ou seguro.', 'no_bills_imperative_default' => 'Você tem essas faturas regulares? Crie uma fatura e acompanhe seus pagamentos:', 'no_bills_create_default' => 'Criar uma fatura', + + // recurring transactions + 'recurrences' => 'Recurring transactions', + 'no_recurring_title_default' => 'Let\'s create a recurring transaction!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Create a recurring transaction', + 'make_new_recurring' => 'Create a recurring transaction', + 'recurring_daily' => 'Every day', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Related transactions', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Tags', + 'recurring_meta_field_notes' => 'Notes', + 'recurring_meta_field_bill_id' => 'Bill', + 'recurring_meta_field_piggy_bank_id' => 'Piggy bank', + 'create_new_recurrence' => 'Create new recurring transaction', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/pt_BR/form.php b/resources/lang/pt_BR/form.php index f31a9d819a..1061a841a0 100644 --- a/resources/lang/pt_BR/form.php +++ b/resources/lang/pt_BR/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Nome do banco', - 'bank_balance' => 'Saldo', - 'savings_balance' => 'Salda da Poupança', - 'credit_card_limit' => 'Limite do Cartão de Crédito', - 'automatch' => 'Equivale automaticamente', - 'skip' => 'Pular', - 'name' => 'Nome', - 'active' => 'Ativar', - 'amount_min' => 'Valor Mínimo', - 'amount_max' => 'Valor Máximo', - 'match' => 'Corresponde em', - 'strict' => 'Modo estrito', - 'repeat_freq' => 'Repetições', - 'journal_currency_id' => 'Moeda', - 'currency_id' => 'Moeda', - 'transaction_currency_id' => 'Moeda', - 'external_ip' => 'O IP externo do seu servidor', - 'attachments' => 'Anexos', - 'journal_amount' => 'Quantia', - 'journal_source_account_name' => 'Conta de receita (fonte)', - 'journal_source_account_id' => 'Conta de ativo (fonte)', - 'BIC' => 'BIC', - 'verify_password' => 'Verificação da segurança da senha', - 'source_account' => 'Conta de origem', - 'destination_account' => 'Conta de destino', - 'journal_destination_account_id' => 'Conta de ativo (destino)', - 'asset_destination_account' => 'Conta de ativo (destino)', - 'asset_source_account' => 'Conta de ativo (fonte)', - 'journal_description' => 'Descrição', - 'note' => 'Notas', - 'split_journal' => 'Dividir essa transação', - 'split_journal_explanation' => 'Dividir essa transação em várias partes', - 'currency' => 'Moeda', - 'account_id' => 'Conta de ativo', - 'budget_id' => 'Orçamento', - 'openingBalance' => 'Saldo inicial', - 'tagMode' => 'Modo de tag', - 'tag_position' => 'Localização do indexador', - 'virtualBalance' => 'Saldo virtual', - 'targetamount' => 'Valor alvo', - 'accountRole' => 'Tipo de conta', - 'openingBalanceDate' => 'Data do Saldo inicial', - 'ccType' => 'Plano de pagamento do Cartão de Crédito', - 'ccMonthlyPaymentDate' => 'Data do pagamento mensal do Cartão de Crédito', - 'piggy_bank_id' => 'Cofrinho', - 'returnHere' => 'Retornar aqui', - 'returnHereExplanation' => 'Depois de armazenar, retorne aqui para criar outro.', - 'returnHereUpdateExplanation' => 'Depois da atualização, retorne aqui', - 'description' => 'Descrição', - 'expense_account' => 'Conta de Despesa', - 'revenue_account' => 'Conta de Receita', - 'decimal_places' => 'Casas décimais', - 'exchange_rate_instruction' => 'Moedas estrangeiras', - 'source_amount' => 'Quantidade (fonte)', - 'destination_amount' => 'Quantidade (destino)', - 'native_amount' => 'Montante original', - 'new_email_address' => 'Novo endereço de e-mail', - 'verification' => 'Verificação', - 'api_key' => 'Chave da API', - 'remember_me' => 'Lembrar-me', + 'bank_name' => 'Nome do banco', + 'bank_balance' => 'Saldo', + 'savings_balance' => 'Salda da Poupança', + 'credit_card_limit' => 'Limite do Cartão de Crédito', + 'automatch' => 'Equivale automaticamente', + 'skip' => 'Pular', + 'name' => 'Nome', + 'active' => 'Ativar', + 'amount_min' => 'Valor Mínimo', + 'amount_max' => 'Valor Máximo', + 'match' => 'Corresponde em', + 'strict' => 'Modo estrito', + 'repeat_freq' => 'Repetições', + 'journal_currency_id' => 'Moeda', + 'currency_id' => 'Moeda', + 'transaction_currency_id' => 'Moeda', + 'external_ip' => 'O IP externo do seu servidor', + 'attachments' => 'Anexos', + 'journal_amount' => 'Quantia', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Verificação da segurança da senha', + 'source_account' => 'Conta de origem', + 'destination_account' => 'Conta de destino', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Conta de ativo (destino)', + 'asset_source_account' => 'Conta de ativo (fonte)', + 'journal_description' => 'Descrição', + 'note' => 'Notas', + 'split_journal' => 'Dividir essa transação', + 'split_journal_explanation' => 'Dividir essa transação em várias partes', + 'currency' => 'Moeda', + 'account_id' => 'Conta de ativo', + 'budget_id' => 'Orçamento', + 'openingBalance' => 'Saldo inicial', + 'tagMode' => 'Modo de tag', + 'tag_position' => 'Localização do indexador', + 'virtualBalance' => 'Saldo virtual', + 'targetamount' => 'Valor alvo', + 'accountRole' => 'Tipo de conta', + 'openingBalanceDate' => 'Data do Saldo inicial', + 'ccType' => 'Plano de pagamento do Cartão de Crédito', + 'ccMonthlyPaymentDate' => 'Data do pagamento mensal do Cartão de Crédito', + 'piggy_bank_id' => 'Cofrinho', + 'returnHere' => 'Retornar aqui', + 'returnHereExplanation' => 'Depois de armazenar, retorne aqui para criar outro.', + 'returnHereUpdateExplanation' => 'Depois da atualização, retorne aqui', + 'description' => 'Descrição', + 'expense_account' => 'Conta de Despesa', + 'revenue_account' => 'Conta de Receita', + 'decimal_places' => 'Casas décimais', + 'exchange_rate_instruction' => 'Moedas estrangeiras', + 'source_amount' => 'Quantidade (fonte)', + 'destination_amount' => 'Quantidade (destino)', + 'native_amount' => 'Montante original', + 'new_email_address' => 'Novo endereço de e-mail', + 'verification' => 'Verificação', + 'api_key' => 'Chave da API', + 'remember_me' => 'Lembrar-me', 'source_account_asset' => 'Conta de origem (conta de ativo)', 'destination_account_expense' => 'Conta de destino (conta de despesa)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Converter o depósito', 'convert_Transfer' => 'Converter a transferência', - 'amount' => 'Valor', - 'foreign_amount' => 'Montante em moeda estrangeira', - 'existing_attachments' => 'Anexos existentes', - 'date' => 'Data', - 'interest_date' => 'Data de interesse', - 'book_date' => 'Data reserva', - 'process_date' => 'Data de processamento', - 'category' => 'Categoria', - 'tags' => 'Etiquetas', - 'deletePermanently' => 'Apagar permanentemente', - 'cancel' => 'Cancelar', - 'targetdate' => 'Data Alvo', - 'startdate' => 'Data de Início', - 'tag' => 'Etiqueta', - 'under' => 'Debaixo', - 'symbol' => 'Símbolo', - 'code' => 'Código', - 'iban' => 'IBAN', - 'accountNumber' => 'Número de conta', - 'creditCardNumber' => 'Número do cartão de crédito', - 'has_headers' => 'Cabeçalhos', - 'date_format' => 'Formato da Data', - 'specifix' => 'Banco- ou arquivo específico corrigídos', - 'attachments[]' => 'Anexos', - 'store_new_withdrawal' => 'Armazenar nova retirada', - 'store_new_deposit' => 'Armazenar novo depósito', - 'store_new_transfer' => 'Armazenar nova transferência', - 'add_new_withdrawal' => 'Adicionar uma nova retirada', - 'add_new_deposit' => 'Adicionar um novo depósito', - 'add_new_transfer' => 'Adicionar uma nova transferência', - 'title' => 'Título', - 'notes' => 'Notas', - 'filename' => 'Nome do arquivo', - 'mime' => 'Tipo do Arquivo (MIME)', - 'size' => 'Tamanho', - 'trigger' => 'Disparo', - 'stop_processing' => 'Parar processamento', - 'start_date' => 'Início do intervalo', - 'end_date' => 'Final do intervalo', - 'export_start_range' => 'Início do intervalo de exportação', - 'export_end_range' => 'Fim do intervalo de exportação', - 'export_format' => 'Formato do arquivo', - 'include_attachments' => 'Incluir anexos enviados', - 'include_old_uploads' => 'Incluir dados importados', - 'accounts' => 'Exportar transações destas contas', - 'delete_account' => 'Apagar conta ":name"', - 'delete_bill' => 'Apagar fatura ":name"', - 'delete_budget' => 'Excluir o orçamento ":name"', - 'delete_category' => 'Excluir categoria ":name"', - 'delete_currency' => 'Excluir moeda ":name"', - 'delete_journal' => 'Excluir a transação com a descrição ":description"', - 'delete_attachment' => 'Apagar anexo ":name"', - 'delete_rule' => 'Excluir regra ":title"', - 'delete_rule_group' => 'Exclua o grupo de regras ":title"', - 'delete_link_type' => 'Excluir tipo de link ":name"', - 'delete_user' => 'Excluir o usuário ":email"', - 'user_areYouSure' => 'Se você excluir o usuário ":email", tudo desaparecerá. Não será possível desfazer a ação. Se excluir você mesmo, você perderá acesso total a essa instância do Firefly III.', - 'attachment_areYouSure' => 'Tem certeza que deseja excluir o anexo denominado ":name"?', - 'account_areYouSure' => 'Tem certeza que deseja excluir a conta denominada ":name"?', - 'bill_areYouSure' => 'Você tem certeza que quer apagar a fatura ":name"?', - 'rule_areYouSure' => 'Tem certeza que deseja excluir a regra intitulada ":title"?', - 'ruleGroup_areYouSure' => 'Tem certeza que deseja excluir o grupo de regras intitulado ":title"?', - 'budget_areYouSure' => 'Tem certeza que deseja excluir o orçamento chamado ":name"?', - 'category_areYouSure' => 'Tem certeza que deseja excluir a categoria com o nome ":name"?', - 'currency_areYouSure' => 'Tem certeza que deseja excluir a moeda chamada ":name"?', - 'piggyBank_areYouSure' => 'Tem certeza que deseja excluir o cofrinho chamado ":name"?', - 'journal_areYouSure' => 'Tem certeza que deseja excluir a transação descrita ":description"?', - 'mass_journal_are_you_sure' => 'Tem a certeza que pretende apagar estas transações?', - 'tag_areYouSure' => 'Você tem certeza que quer apagar a tag ":tag"?', - 'journal_link_areYouSure' => 'Tem certeza que deseja excluir a ligação entre :source e :destination?', - 'linkType_areYouSure' => 'Tem certeza que deseja excluir o tipo de link ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Exclusão de dados do Firefly III são permanentes e não podem ser desfeitos.', - 'mass_make_selection' => 'Você ainda pode evitar que itens sejam excluídos, removendo a caixa de seleção.', - 'delete_all_permanently' => 'Exclua os selecionados permanentemente', - 'update_all_journals' => 'Atualizar essas transações', - 'also_delete_transactions' => 'A única transação ligada a essa conta será excluída também.|Todas as :count transações ligadas a esta conta serão excluídas também.', - 'also_delete_connections' => 'A única transação relacionada com este tipo de link vai perder a conexão. | Todas as transações de :count ligadas com este tipo de link vão perder sua conexão.', - 'also_delete_rules' => 'A única regra que ligado a este grupo de regras será excluída também.|Todos as :count regras ligadas a este grupo de regras serão excluídas também.', - 'also_delete_piggyBanks' => 'O único cofrinho conectado a essa conta será excluído também.|Todos os :count cofrinhos conectados a esta conta serão excluídos também.', - 'bill_keep_transactions' => 'A única transação a esta conta não será excluída.|Todos as :count transações conectadas a esta fatura não serão excluídos.', - 'budget_keep_transactions' => 'A única transação conectada a este orçamento não será excluída.|Todos :count transações ligadas a este orçamento não serão excluídos.', - 'category_keep_transactions' => 'A única transação ligada a esta categoria não será excluída.|Todos :count transações ligadas a esta categoria não serão excluídos.', - 'tag_keep_transactions' => 'A única transação ligada a essa marca não será excluída.|Todos :count transações ligadas a essa marca não serão excluídos.', - 'check_for_updates' => 'Buscar atualizações', + 'amount' => 'Valor', + 'foreign_amount' => 'Montante em moeda estrangeira', + 'existing_attachments' => 'Anexos existentes', + 'date' => 'Data', + 'interest_date' => 'Data de interesse', + 'book_date' => 'Data reserva', + 'process_date' => 'Data de processamento', + 'category' => 'Categoria', + 'tags' => 'Etiquetas', + 'deletePermanently' => 'Apagar permanentemente', + 'cancel' => 'Cancelar', + 'targetdate' => 'Data Alvo', + 'startdate' => 'Data de Início', + 'tag' => 'Etiqueta', + 'under' => 'Debaixo', + 'symbol' => 'Símbolo', + 'code' => 'Código', + 'iban' => 'IBAN', + 'accountNumber' => 'Número de conta', + 'creditCardNumber' => 'Número do cartão de crédito', + 'has_headers' => 'Cabeçalhos', + 'date_format' => 'Formato da Data', + 'specifix' => 'Banco- ou arquivo específico corrigídos', + 'attachments[]' => 'Anexos', + 'store_new_withdrawal' => 'Armazenar nova retirada', + 'store_new_deposit' => 'Armazenar novo depósito', + 'store_new_transfer' => 'Armazenar nova transferência', + 'add_new_withdrawal' => 'Adicionar uma nova retirada', + 'add_new_deposit' => 'Adicionar um novo depósito', + 'add_new_transfer' => 'Adicionar uma nova transferência', + 'title' => 'Título', + 'notes' => 'Notas', + 'filename' => 'Nome do arquivo', + 'mime' => 'Tipo do Arquivo (MIME)', + 'size' => 'Tamanho', + 'trigger' => 'Disparo', + 'stop_processing' => 'Parar processamento', + 'start_date' => 'Início do intervalo', + 'end_date' => 'Final do intervalo', + 'export_start_range' => 'Início do intervalo de exportação', + 'export_end_range' => 'Fim do intervalo de exportação', + 'export_format' => 'Formato do arquivo', + 'include_attachments' => 'Incluir anexos enviados', + 'include_old_uploads' => 'Incluir dados importados', + 'accounts' => 'Exportar transações destas contas', + 'delete_account' => 'Apagar conta ":name"', + 'delete_bill' => 'Apagar fatura ":name"', + 'delete_budget' => 'Excluir o orçamento ":name"', + 'delete_category' => 'Excluir categoria ":name"', + 'delete_currency' => 'Excluir moeda ":name"', + 'delete_journal' => 'Excluir a transação com a descrição ":description"', + 'delete_attachment' => 'Apagar anexo ":name"', + 'delete_rule' => 'Excluir regra ":title"', + 'delete_rule_group' => 'Exclua o grupo de regras ":title"', + 'delete_link_type' => 'Excluir tipo de link ":name"', + 'delete_user' => 'Excluir o usuário ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Se você excluir o usuário ":email", tudo desaparecerá. Não será possível desfazer a ação. Se excluir você mesmo, você perderá acesso total a essa instância do Firefly III.', + 'attachment_areYouSure' => 'Tem certeza que deseja excluir o anexo denominado ":name"?', + 'account_areYouSure' => 'Tem certeza que deseja excluir a conta denominada ":name"?', + 'bill_areYouSure' => 'Você tem certeza que quer apagar a fatura ":name"?', + 'rule_areYouSure' => 'Tem certeza que deseja excluir a regra intitulada ":title"?', + 'ruleGroup_areYouSure' => 'Tem certeza que deseja excluir o grupo de regras intitulado ":title"?', + 'budget_areYouSure' => 'Tem certeza que deseja excluir o orçamento chamado ":name"?', + 'category_areYouSure' => 'Tem certeza que deseja excluir a categoria com o nome ":name"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => 'Tem certeza que deseja excluir a moeda chamada ":name"?', + 'piggyBank_areYouSure' => 'Tem certeza que deseja excluir o cofrinho chamado ":name"?', + 'journal_areYouSure' => 'Tem certeza que deseja excluir a transação descrita ":description"?', + 'mass_journal_are_you_sure' => 'Tem a certeza que pretende apagar estas transações?', + 'tag_areYouSure' => 'Você tem certeza que quer apagar a tag ":tag"?', + 'journal_link_areYouSure' => 'Tem certeza que deseja excluir a ligação entre :source e :destination?', + 'linkType_areYouSure' => 'Tem certeza que deseja excluir o tipo de link ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Exclusão de dados do Firefly III são permanentes e não podem ser desfeitos.', + 'mass_make_selection' => 'Você ainda pode evitar que itens sejam excluídos, removendo a caixa de seleção.', + 'delete_all_permanently' => 'Exclua os selecionados permanentemente', + 'update_all_journals' => 'Atualizar essas transações', + 'also_delete_transactions' => 'A única transação ligada a essa conta será excluída também.|Todas as :count transações ligadas a esta conta serão excluídas também.', + 'also_delete_connections' => 'A única transação relacionada com este tipo de link vai perder a conexão. | Todas as transações de :count ligadas com este tipo de link vão perder sua conexão.', + 'also_delete_rules' => 'A única regra que ligado a este grupo de regras será excluída também.|Todos as :count regras ligadas a este grupo de regras serão excluídas também.', + 'also_delete_piggyBanks' => 'O único cofrinho conectado a essa conta será excluído também.|Todos os :count cofrinhos conectados a esta conta serão excluídos também.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Buscar atualizações', 'email' => 'E-mail', 'password' => 'Senha', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Código do país', 'provider_code' => 'Banco ou provedor de dados', - 'due_date' => 'Data de vencimento', - 'payment_date' => 'Data de pagamento', - 'invoice_date' => 'Data da Fatura', - 'internal_reference' => 'Referência interna', - 'inward' => 'Descrição interna', - 'outward' => 'Descrição externa', - 'rule_group_id' => 'Grupo de regras', + 'due_date' => 'Data de vencimento', + 'payment_date' => 'Data de pagamento', + 'invoice_date' => 'Data da Fatura', + 'internal_reference' => 'Referência interna', + 'inward' => 'Descrição interna', + 'outward' => 'Descrição externa', + 'rule_group_id' => 'Grupo de regras', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/pt_BR/import.php b/resources/lang/pt_BR/import.php index 377665273f..5766b66116 100644 --- a/resources/lang/pt_BR/import.php +++ b/resources/lang/pt_BR/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/pt_BR/list.php b/resources/lang/pt_BR/list.php index f65a5eee64..5c90f01f42 100644 --- a/resources/lang/pt_BR/list.php +++ b/resources/lang/pt_BR/list.php @@ -123,4 +123,9 @@ return [ 'spectre_last_use' => 'Last login', 'spectre_status' => 'Status', 'bunq_payment_id' => 'bunq payment ID', + 'repetitions' => 'Repetitions', + 'title' => 'Title', + 'transaction_s' => 'Transaction(s)', + 'field' => 'Field', + 'value' => 'Value', ]; diff --git a/resources/lang/pt_BR/validation.php b/resources/lang/pt_BR/validation.php index 841f7c2d55..eec02240af 100644 --- a/resources/lang/pt_BR/validation.php +++ b/resources/lang/pt_BR/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Este não é um válido IBAN.', - 'source_equals_destination' => 'A conta de origem é igual à conta de destino', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Parece que este número de conta já está em uso.', 'unique_iban_for_user' => 'Parece que este IBAN já está em uso.', 'deleted_user' => 'Devido a restrições de segurança, você não pode registrar usando este endereço de e-mail.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Arquivo carregado com sucesso ":name".', 'must_exist' => 'O ID no campo :attribute não existe no banco de dados.', 'all_accounts_equal' => 'Todas as contas neste campo devem ser iguais.', - 'invalid_selection' => 'Sua seleção é inválida', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'Esse valor é inválido para este campo.', 'at_least_one_transaction' => 'Precisa de ao menos uma transação.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'O conteúdo deste campo é inválido sem informações de moeda.', 'equal_description' => 'A descrição da transação não pode ser igual à descrição global.', 'file_invalid_mime' => 'Arquivo ":name" é do tipo ":mime" que não é aceito como um novo upload.', 'file_too_large' => 'Arquivo ":name" é muito grande.', - 'belongs_to_user' => 'O valor de :attribute é desconhecido', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => 'O campo :attribute deve ser aceito.', 'bic' => 'Este não é um BIC válido.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute deve ser maior que zero.', 'active_url' => 'O campo :attribute não contém um URL válido.', 'after' => 'O campo :attribute deverá conter uma data posterior a :date.', @@ -53,8 +59,8 @@ return [ 'array' => 'O campo :attribute precisa ser um conjunto.', 'unique_for_user' => 'Já existe uma entrada com este :attribute.', 'before' => 'O campo :attribute deverá conter uma data anterior a :date.', - 'unique_object_for_user' => 'Este nome já está em uso', - 'unique_account_for_user' => 'Este nome de conta já está em uso', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => 'O campo :attribute deverá ter um valor entre :min - :max.', 'between.file' => 'O campo :attribute deverá ter um tamanho entre :min - :max kilobytes.', 'between.string' => 'O campo :attribute deverá conter entre :min - :max caracteres.', @@ -85,6 +91,9 @@ return [ 'min.array' => 'O campo :attribute deve ter no mínimo :min itens.', 'not_in' => 'O campo :attribute contém um valor inválido.', 'numeric' => 'O campo :attribute deverá conter um valor numérico.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'O formato do valor para o campo :attribute é inválido.', 'required' => 'O campo :attribute é obrigatório.', 'required_if' => 'O campo :attribute é obrigatório quando o valor do campo :other é igual a :value.', @@ -109,9 +118,12 @@ return [ 'file' => 'O :attribute deve ser um arquivo.', 'in_array' => 'O campo :attribute não existe em :other.', 'present' => 'O campo :attribute deve estar presente.', - 'amount_zero' => 'A quantidade total não pode ser zero', + 'amount_zero' => 'The total amount cannot be zero.', 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => 'endereço de e-mail', 'description' => 'descrição', diff --git a/resources/lang/ru_RU/config.php b/resources/lang/ru_RU/config.php index a524e3644c..58b0bfdd02 100644 --- a/resources/lang/ru_RU/config.php +++ b/resources/lang/ru_RU/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'ru', - 'locale' => 'ru, Russian, ru_RU, ru_RU.utf8, ru_RU.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => 'Неделя %W, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'Do MMMM YYYY', - 'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Неделя] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'ru', + 'locale' => 'ru, Russian, ru_RU, ru_RU.utf8, ru_RU.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A, %B %e %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => 'Неделя %W, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'Do MMMM YYYY', + 'date_time_js' => 'Do MMMM YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Неделя] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Понедельник', + 'dow_2' => 'Вторник', + 'dow_3' => 'Среда', + 'dow_4' => 'Четверг', + 'dow_5' => 'Пятница', + 'dow_6' => 'Суббота', + 'dow_7' => 'Воскресенье', ]; diff --git a/resources/lang/ru_RU/demo.php b/resources/lang/ru_RU/demo.php index 293ba441f0..8eb16e401b 100644 --- a/resources/lang/ru_RU/demo.php +++ b/resources/lang/ru_RU/demo.php @@ -34,4 +34,6 @@ return [ 'transactions-index' => 'Эти расходы, доходы и переводы не очень интересны. Они были созданы автоматически.', 'piggy-banks-index' => 'Как вы можете видеть, здесь есть три копилки. Используйте кнопки «плюс» и «минус», чтобы влиять на количество денег в каждой копилке. Нажмите название копилки, чтобы увидеть её настройки.', 'import-index' => 'В Firefly III можно импортировать любой CSV-файл. Также поддерживается импорт данных из bunq и Spectre. Другие банки и финансовые агрегаторы будут реализованы в будущем. Однако, как демо-пользователь, вы можете видеть только как работает «поддельный»-провайдер. Он будет генерировать некоторые случайные транзакции, чтобы показать вам, как работает этот процесс.', + 'recurring-index' => 'Пожалуйста, обратите внимание, что эта функция находится в активной разработке и может работать не совcем так, как вы ожидаете.', + 'recurring-create' => 'Пожалуйста, обратите внимание, что эта функция находится в активной разработке и может работать не совcем так, как вы ожидаете.', ]; diff --git a/resources/lang/ru_RU/firefly.php b/resources/lang/ru_RU/firefly.php index 382410ca5c..c18e2716da 100644 --- a/resources/lang/ru_RU/firefly.php +++ b/resources/lang/ru_RU/firefly.php @@ -465,6 +465,7 @@ return [ 'pref_two_factor_auth_code_help' => 'Отсканируйте QR-код с помощью приложения на телефоне, например Authy или Google Authenticator, и введите сгенерированный код.', 'pref_two_factor_auth_reset_code' => 'Сбросить код верификации', 'pref_two_factor_auth_disable_2fa' => 'Выключить 2FA', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Сохранить настройки', 'saved_preferences' => 'Настройки сохранены!', 'preferences_general' => 'Основные', @@ -820,7 +821,7 @@ return [ 'language' => 'Язык', 'new_savings_account' => 'сберегательный счёт в :bank_name', 'cash_wallet' => 'Кошелёк с наличными', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'currency_not_present' => 'Если ваша основная валюта ещё не указана, не волнуйтесь. Вы можете создать собственные валюты в разделе Параметры > Валюты.', // home page: 'yourAccounts' => 'Ваши счета', @@ -900,7 +901,6 @@ return [ 'balanceEnd' => 'Остаток на конец периода', 'splitByAccount' => 'Разделить по разным счетам', 'coveredWithTags' => 'Присвоены метки', - 'leftUnbalanced' => 'Осталось вне баланса', 'leftInBudget' => 'Осталось в бюджете', 'sumOfSums' => 'Сумма сумм', 'noCategory' => '(без категории)', @@ -1134,6 +1134,8 @@ return [ 'is (partially) refunded by_inward' => '(частично) возвращён', 'is (partially) paid for by_inward' => '(частично) оплачен', 'is (partially) reimbursed by_inward' => '(частично) возмещён', + 'inward_transaction' => 'Inward transaction', + 'outward_transaction' => 'Outward transaction', 'relates to_outward' => 'относится к', '(partially) refunds_outward' => '(частично) возвращены', '(partially) pays for_outward' => '(частично) оплачены', @@ -1155,8 +1157,9 @@ return [ 'cannot_convert_split_journal' => 'Невозможно преобразовать раздельную транзакцию', // Import page (general strings only) - 'import_index_title' => 'Импорт данных в Firefly III', + 'import_index_title' => 'Импорт транзакций в Firefly III', 'import_data' => 'Импорт данных', + 'import_transactions' => 'Импорт транзакций', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Эта функция недоступна, если вы используете Firefly III в среде Sandstorm.io.', @@ -1206,4 +1209,68 @@ return [ 'no_bills_intro_default' => 'У вас пока нет счетов на оплату. Вы можете создавать счета для отслеживания регулярных расходов, таких как арендная плата или страхование.', 'no_bills_imperative_default' => 'У вас есть такие регулярные платежи? Создайте счёт на оплату и отслеживайте свои платежи:', 'no_bills_create_default' => 'Создать счет к оплате', + + // recurring transactions + 'recurrences' => 'Повторяющиеся транзакции', + 'no_recurring_title_default' => 'Давайте создадим повторяющуюся транзакцию!', + 'no_recurring_intro_default' => 'You have no recurring transactions yet. You can use these to make Firefly III automatically create transactions for you.', + 'no_recurring_imperative_default' => 'This is a pretty advanced feature but it can be extremely useful. Make sure you read the documentation (?)-icon in the top right corner) before you continue.', + 'no_recurring_create_default' => 'Создать повторяющуюся транзакцию', + 'make_new_recurring' => 'Создать повторяющуюся транзакцию', + 'recurring_daily' => 'Каждый день', + 'recurring_weekly' => 'Каждую неделю в :weekday', + 'recurring_monthly' => 'Каждый месяц в :dayOfMonth(st/nd/rd/th) день', + 'recurring_ndom' => 'Каждый месяц в :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Каждый год на :date', + 'overview_for_recurrence' => 'Обзор повторяющейся транзакции ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'Связанные транзакции', + 'expected_Withdrawals' => 'Просроченные расходы', + 'expected_Deposits' => 'Просроченные доходы', + 'expected_Transfers' => 'Просроченные переводы', + 'created_Withdrawals' => 'Расходы созданы', + 'created_Deposits' => 'Доходы созданы', + 'created_Transfers' => 'Переводы созданы', + 'created_from_recurrence' => 'Создано из повторяющейся транзакции ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Метки', + 'recurring_meta_field_notes' => 'Примечания', + 'recurring_meta_field_bill_id' => 'Счёт к оплате', + 'recurring_meta_field_piggy_bank_id' => 'Копилка', + 'create_new_recurrence' => 'Создать новую повторяющуюся транзакцию', + 'help_first_date' => 'Укажите первое ожидаемое повторение. Оно должно быть в будущем.', + 'help_first_date_no_past' => 'Укажите первое ожидаемое повторение. Firefly III не будет создавать транзакции ранее этой даты.', + 'no_currency' => '(нет валюты)', + 'mandatory_for_recurring' => 'Обязательные сведения о повторении', + 'mandatory_for_transaction' => 'Обязательные сведения о транзакции', + 'optional_for_recurring' => 'Опциональные сведения о повторении', + 'optional_for_transaction' => 'Опциональные сведения о транзакции', + 'change_date_other_options' => 'Измените "первую дату", чтобы увидеть больше опций.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Щёлкните здесь, чтобы открыт календарь и указать, когда транзакция будет повторяться.', + 'repeat_forever' => 'Повторять всегда', + 'repeat_until_date' => 'Повторять до указанной даты', + 'repeat_times' => 'Повторять указанное число раз', + 'recurring_skips_one' => 'Другой период', + 'recurring_skips_more' => 'Пропустить :count повторов', + 'store_new_recurrence' => 'Сохранить повторяющуюся транзакцию', + 'stored_new_recurrence' => 'Повторяющаяся транзакция ":title" успешно сохранена.', + 'edit_recurrence' => 'Изменить повторяющуюся транзакцию ":title"', + 'recurring_repeats_until' => 'Повторять до :date', + 'recurring_repeats_forever' => 'Повторять всегда', + 'recurring_repeats_x_times' => 'Повторить :count раз(а)', + 'update_recurrence' => 'Обновить повторяющуюся транзакцию', + 'updated_recurrence' => 'Повторяющаяся транзакция ":title" обновлена', + 'recurrence_is_inactive' => 'Эта повторяющаяся транзакция не активна и не создаёт новые транзакции.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/ru_RU/form.php b/resources/lang/ru_RU/form.php index a4b4ef54cf..625439e845 100644 --- a/resources/lang/ru_RU/form.php +++ b/resources/lang/ru_RU/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Название банка', - 'bank_balance' => 'Бaлaнc', - 'savings_balance' => 'Сберегательный баланс', - 'credit_card_limit' => 'Лимит кредитной карты', - 'automatch' => 'Автоматическое сопоставление', - 'skip' => 'Пропустить', - 'name' => 'Название', - 'active' => 'Активный', - 'amount_min' => 'Минимальная сумма', - 'amount_max' => 'Максимальная сумма', - 'match' => 'Ключи для связи', - 'strict' => 'Строгий режим', - 'repeat_freq' => 'Повторы', - 'journal_currency_id' => 'Валюта', - 'currency_id' => 'Валюта', - 'transaction_currency_id' => 'Валюта', - 'external_ip' => 'Внешний IP-адрес вашего сервера', - 'attachments' => 'Вложения', - 'journal_amount' => 'Сумма', - 'journal_source_account_name' => 'Доходный счет (источник)', - 'journal_source_account_id' => 'Основной счёт (источник)', - 'BIC' => 'BIC', - 'verify_password' => 'Проверка безопасности паролей', - 'source_account' => 'Исходный счёт', - 'destination_account' => 'Счёт назначения', - 'journal_destination_account_id' => 'Основной счёт (назначение)', - 'asset_destination_account' => 'Основной счёт (назначение)', - 'asset_source_account' => 'Основной счёт (источник)', - 'journal_description' => 'Описание', - 'note' => 'Заметки', - 'split_journal' => 'Разделить эту транзакцию', - 'split_journal_explanation' => 'Разделить эту транзакцию на несколько частей', - 'currency' => 'Валюта', - 'account_id' => 'Основной счёт', - 'budget_id' => 'Бюджет', - 'openingBalance' => 'Начальный баланс', - 'tagMode' => 'Режим метки', - 'tag_position' => 'Расположение метки', - 'virtualBalance' => 'Виртуальный баланс', - 'targetamount' => 'Целевая сумма', - 'accountRole' => 'Роль учётной записи', - 'openingBalanceDate' => 'Дата начального баланса', - 'ccType' => 'План оплаты по кредитной карте', - 'ccMonthlyPaymentDate' => 'Дата ежемесячного платежа по кредитной карте', - 'piggy_bank_id' => 'Копилка', - 'returnHere' => 'Вернуться сюда', - 'returnHereExplanation' => 'После сохранения вернуться сюда и создать ещё одну аналогичную запись.', - 'returnHereUpdateExplanation' => 'Вернуться на эту страницу после обновления.', - 'description' => 'Описание', - 'expense_account' => 'Счет расходов', - 'revenue_account' => 'Доходный счет', - 'decimal_places' => 'Количество цифр после точки', - 'exchange_rate_instruction' => 'Иностранные валюты', - 'source_amount' => 'Сумма (источник)', - 'destination_amount' => 'Сумма (назначение)', - 'native_amount' => 'Собственная сумма', - 'new_email_address' => 'Новый адрес электронной почты', - 'verification' => 'Проверка', - 'api_key' => 'API-ключ', - 'remember_me' => 'Запомнить меня', + 'bank_name' => 'Название банка', + 'bank_balance' => 'Бaлaнc', + 'savings_balance' => 'Сберегательный баланс', + 'credit_card_limit' => 'Лимит кредитной карты', + 'automatch' => 'Автоматическое сопоставление', + 'skip' => 'Пропустить', + 'name' => 'Название', + 'active' => 'Активный', + 'amount_min' => 'Минимальная сумма', + 'amount_max' => 'Максимальная сумма', + 'match' => 'Ключи для связи', + 'strict' => 'Строгий режим', + 'repeat_freq' => 'Повторы', + 'journal_currency_id' => 'Валюта', + 'currency_id' => 'Валюта', + 'transaction_currency_id' => 'Валюта', + 'external_ip' => 'Внешний IP-адрес вашего сервера', + 'attachments' => 'Вложения', + 'journal_amount' => 'Сумма', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Проверка безопасности паролей', + 'source_account' => 'Исходный счёт', + 'destination_account' => 'Счёт назначения', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Основной счёт (назначение)', + 'asset_source_account' => 'Основной счёт (источник)', + 'journal_description' => 'Описание', + 'note' => 'Заметки', + 'split_journal' => 'Разделить эту транзакцию', + 'split_journal_explanation' => 'Разделить эту транзакцию на несколько частей', + 'currency' => 'Валюта', + 'account_id' => 'Основной счёт', + 'budget_id' => 'Бюджет', + 'openingBalance' => 'Начальный баланс', + 'tagMode' => 'Режим метки', + 'tag_position' => 'Расположение метки', + 'virtualBalance' => 'Виртуальный баланс', + 'targetamount' => 'Целевая сумма', + 'accountRole' => 'Роль учётной записи', + 'openingBalanceDate' => 'Дата начального баланса', + 'ccType' => 'План оплаты по кредитной карте', + 'ccMonthlyPaymentDate' => 'Дата ежемесячного платежа по кредитной карте', + 'piggy_bank_id' => 'Копилка', + 'returnHere' => 'Вернуться сюда', + 'returnHereExplanation' => 'После сохранения вернуться сюда и создать ещё одну аналогичную запись.', + 'returnHereUpdateExplanation' => 'Вернуться на эту страницу после обновления.', + 'description' => 'Описание', + 'expense_account' => 'Счет расходов', + 'revenue_account' => 'Доходный счет', + 'decimal_places' => 'Количество цифр после точки', + 'exchange_rate_instruction' => 'Иностранные валюты', + 'source_amount' => 'Сумма (источник)', + 'destination_amount' => 'Сумма (назначение)', + 'native_amount' => 'Собственная сумма', + 'new_email_address' => 'Новый адрес электронной почты', + 'verification' => 'Проверка', + 'api_key' => 'API-ключ', + 'remember_me' => 'Запомнить меня', 'source_account_asset' => 'Исходный счёт (основной счёт)', 'destination_account_expense' => 'Счёт назначения (счёт расхода)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Конвертировать доход', 'convert_Transfer' => 'Конвертировать перевод', - 'amount' => 'Сумма', - 'foreign_amount' => 'Сумму в иностранной валюте', - 'existing_attachments' => 'Существующие вложения', - 'date' => 'Дата', - 'interest_date' => 'Дата выплаты', - 'book_date' => 'Дата бронирования', - 'process_date' => 'Дата обработки', - 'category' => 'Категория', - 'tags' => 'Метки', - 'deletePermanently' => 'Удалить навсегда', - 'cancel' => 'Отмена', - 'targetdate' => 'Намеченная дата', - 'startdate' => 'Дата начала', - 'tag' => 'Тег', - 'under' => 'Под', - 'symbol' => 'Символ', - 'code' => 'Код', - 'iban' => 'IBAN', - 'accountNumber' => 'Номер счета', - 'creditCardNumber' => 'Номер кредитной карты', - 'has_headers' => 'Заголовки', - 'date_format' => 'Формат даты', - 'specifix' => 'Исправления, специфичные для банка или файла', - 'attachments[]' => 'Вложения', - 'store_new_withdrawal' => 'Сохранить новый расход', - 'store_new_deposit' => 'Сохранить новый доход', - 'store_new_transfer' => 'Сохранить новый перевод', - 'add_new_withdrawal' => 'Добавить новый расход', - 'add_new_deposit' => 'Добавить новый доход', - 'add_new_transfer' => 'Добавить новый перевод', - 'title' => 'Заголовок', - 'notes' => 'Заметки', - 'filename' => 'Имя файла', - 'mime' => 'Тип Mime', - 'size' => 'Размер', - 'trigger' => 'Триггер', - 'stop_processing' => 'Остановить обработку', - 'start_date' => 'Начало диапазона', - 'end_date' => 'Конец диапазона', - 'export_start_range' => 'Начало диапазона для экспорта', - 'export_end_range' => 'Конец диапазона для экспорта', - 'export_format' => 'Формат файла', - 'include_attachments' => 'Включить загруженные вложения', - 'include_old_uploads' => 'Включить импортированные данные', - 'accounts' => 'Экспорт транзакций с этих счетов', - 'delete_account' => 'Удалить счёт ":name"', - 'delete_bill' => 'Удаление счёта к оплате ":name"', - 'delete_budget' => 'Удалить бюджет ":name"', - 'delete_category' => 'Удалить категорию ":name"', - 'delete_currency' => 'Удалить валюту ":name"', - 'delete_journal' => 'Удалить транзакцию с описанием ":description"', - 'delete_attachment' => 'Удалить вложение ":name"', - 'delete_rule' => 'Удалить правило ":title"', - 'delete_rule_group' => 'Удалить группу правил ":title"', - 'delete_link_type' => 'Удалить тип ссылки ":name"', - 'delete_user' => 'Удалить пользователя ":email"', - 'user_areYouSure' => 'Если вы удалите пользователя ":email", все данные будут удалены. Это действие нельзя будет отменить. Если вы удалите себя, вы потеряете доступ к этому экземпляру Firefly III.', - 'attachment_areYouSure' => 'Вы действительно хотите удалить вложение с именем ":name"?', - 'account_areYouSure' => 'Вы действительно хотите удалить счёт с именем ":name"?', - 'bill_areYouSure' => 'Вы действительно хотите удалить счёт на оплату с именем ":name"?', - 'rule_areYouSure' => 'Вы действительно хотите удалить правило с названием ":title"?', - 'ruleGroup_areYouSure' => 'Вы действительно хотите удалить группу правил с названием ":title"?', - 'budget_areYouSure' => 'Вы действительно хотите удалить бюджет с именем ":name"?', - 'category_areYouSure' => 'Вы действительно хотите удалить категорию с именем ":name"?', - 'currency_areYouSure' => 'Вы уверены, что хотите удалить валюту ":name"?', - 'piggyBank_areYouSure' => 'Вы уверены, что хотите удалить копилку с именем ":name"?', - 'journal_areYouSure' => 'Вы действительно хотите удалить транзакцию с описанием ":description"?', - 'mass_journal_are_you_sure' => 'Вы действительно хотите удалить эти транзакции?', - 'tag_areYouSure' => 'Вы действительно хотите удалить метку ":tag"?', - 'journal_link_areYouSure' => 'Вы действительно хотите удалить связь между :source и :destination?', - 'linkType_areYouSure' => 'Вы уверены, что хотите удалить тип ссылки ":name" (":inward" / ":outward")?', - 'permDeleteWarning' => 'Удаление информации из Firefly III является постоянным и не может быть отменено.', - 'mass_make_selection' => 'Вы все же можете предотвратить удаление элементов, сняв флажок.', - 'delete_all_permanently' => 'Удалить выбранное навсегда', - 'update_all_journals' => 'Обновить эти транзакции', - 'also_delete_transactions' => 'Будет удалена только транзакция, связанная с этим счётом.|Будут удалены все :count транзакций, связанные с этим счётом.', - 'also_delete_connections' => 'Единственная транзакция, связанная с данным типом ссылки, потеряет это соединение. |Все :count транзакций, связанные с данным типом ссылки, потеряют свои соединения.', - 'also_delete_rules' => 'Единственное правило, связанное с данной группой правил, будет удалено. |Все :count правила, связанные с данной группой правил, будут удалены.', - 'also_delete_piggyBanks' => 'Единственная копилка, связанная с данным счётом, будет удалена.|Все :count копилки, связанные с данным счётом, будут удалены.', - 'bill_keep_transactions' => 'Единственная транзакция, связанная с данным счётом, не будет удалена. |Все :count транзакции, связанные с данным счётом, будут сохранены.', - 'budget_keep_transactions' => 'Единственная транзакция, связанная с данным бюджетом, не будет удалена.|Все :count транзакции, связанные с этим бюджетом, будут сохранены.', - 'category_keep_transactions' => 'Единственная транзакция, связанная с данной категорией, не будет удалена.|Все :count транзакции, связанные с этой категорией, будут сохранены.', - 'tag_keep_transactions' => 'Только транзакция, связанная с этой меткой, будет удалена.|Все :count транзакций, связанные с этой меткой, будут удалены.', - 'check_for_updates' => 'Проверить обновления', + 'amount' => 'Сумма', + 'foreign_amount' => 'Сумму в иностранной валюте', + 'existing_attachments' => 'Существующие вложения', + 'date' => 'Дата', + 'interest_date' => 'Дата выплаты', + 'book_date' => 'Дата бронирования', + 'process_date' => 'Дата обработки', + 'category' => 'Категория', + 'tags' => 'Метки', + 'deletePermanently' => 'Удалить навсегда', + 'cancel' => 'Отмена', + 'targetdate' => 'Намеченная дата', + 'startdate' => 'Дата начала', + 'tag' => 'Тег', + 'under' => 'Под', + 'symbol' => 'Символ', + 'code' => 'Код', + 'iban' => 'IBAN', + 'accountNumber' => 'Номер счета', + 'creditCardNumber' => 'Номер кредитной карты', + 'has_headers' => 'Заголовки', + 'date_format' => 'Формат даты', + 'specifix' => 'Исправления, специфичные для банка или файла', + 'attachments[]' => 'Вложения', + 'store_new_withdrawal' => 'Сохранить новый расход', + 'store_new_deposit' => 'Сохранить новый доход', + 'store_new_transfer' => 'Сохранить новый перевод', + 'add_new_withdrawal' => 'Добавить новый расход', + 'add_new_deposit' => 'Добавить новый доход', + 'add_new_transfer' => 'Добавить новый перевод', + 'title' => 'Заголовок', + 'notes' => 'Заметки', + 'filename' => 'Имя файла', + 'mime' => 'Тип Mime', + 'size' => 'Размер', + 'trigger' => 'Триггер', + 'stop_processing' => 'Остановить обработку', + 'start_date' => 'Начало диапазона', + 'end_date' => 'Конец диапазона', + 'export_start_range' => 'Начало диапазона для экспорта', + 'export_end_range' => 'Конец диапазона для экспорта', + 'export_format' => 'Формат файла', + 'include_attachments' => 'Включить загруженные вложения', + 'include_old_uploads' => 'Включить импортированные данные', + 'accounts' => 'Экспорт транзакций с этих счетов', + 'delete_account' => 'Удалить счёт ":name"', + 'delete_bill' => 'Удаление счёта к оплате ":name"', + 'delete_budget' => 'Удалить бюджет ":name"', + 'delete_category' => 'Удалить категорию ":name"', + 'delete_currency' => 'Удалить валюту ":name"', + 'delete_journal' => 'Удалить транзакцию с описанием ":description"', + 'delete_attachment' => 'Удалить вложение ":name"', + 'delete_rule' => 'Удалить правило ":title"', + 'delete_rule_group' => 'Удалить группу правил ":title"', + 'delete_link_type' => 'Удалить тип ссылки ":name"', + 'delete_user' => 'Удалить пользователя ":email"', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => 'Если вы удалите пользователя ":email", все данные будут удалены. Это действие нельзя будет отменить. Если вы удалите себя, вы потеряете доступ к этому экземпляру Firefly III.', + 'attachment_areYouSure' => 'Вы действительно хотите удалить вложение с именем ":name"?', + 'account_areYouSure' => 'Вы действительно хотите удалить счёт с именем ":name"?', + 'bill_areYouSure' => 'Вы действительно хотите удалить счёт на оплату с именем ":name"?', + 'rule_areYouSure' => 'Вы действительно хотите удалить правило с названием ":title"?', + 'ruleGroup_areYouSure' => 'Вы действительно хотите удалить группу правил с названием ":title"?', + 'budget_areYouSure' => 'Вы действительно хотите удалить бюджет с именем ":name"?', + 'category_areYouSure' => 'Вы действительно хотите удалить категорию с именем ":name"?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => 'Вы уверены, что хотите удалить валюту ":name"?', + 'piggyBank_areYouSure' => 'Вы уверены, что хотите удалить копилку с именем ":name"?', + 'journal_areYouSure' => 'Вы действительно хотите удалить транзакцию с описанием ":description"?', + 'mass_journal_are_you_sure' => 'Вы действительно хотите удалить эти транзакции?', + 'tag_areYouSure' => 'Вы действительно хотите удалить метку ":tag"?', + 'journal_link_areYouSure' => 'Вы действительно хотите удалить связь между :source и :destination?', + 'linkType_areYouSure' => 'Вы уверены, что хотите удалить тип ссылки ":name" (":inward" / ":outward")?', + 'permDeleteWarning' => 'Удаление информации из Firefly III является постоянным и не может быть отменено.', + 'mass_make_selection' => 'Вы все же можете предотвратить удаление элементов, сняв флажок.', + 'delete_all_permanently' => 'Удалить выбранное навсегда', + 'update_all_journals' => 'Обновить эти транзакции', + 'also_delete_transactions' => 'Будет удалена только транзакция, связанная с этим счётом.|Будут удалены все :count транзакций, связанные с этим счётом.', + 'also_delete_connections' => 'Единственная транзакция, связанная с данным типом ссылки, потеряет это соединение. |Все :count транзакций, связанные с данным типом ссылки, потеряют свои соединения.', + 'also_delete_rules' => 'Единственное правило, связанное с данной группой правил, будет удалено. |Все :count правила, связанные с данной группой правил, будут удалены.', + 'also_delete_piggyBanks' => 'Единственная копилка, связанная с данным счётом, будет удалена.|Все :count копилки, связанные с данным счётом, будут удалены.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Проверить обновления', 'email' => 'Адрес электронной почты', 'password' => 'Пароль', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Код страны', 'provider_code' => 'Банк или поставщик данных', - 'due_date' => 'Срок', - 'payment_date' => 'Дата платежа', - 'invoice_date' => 'Дата выставления счёта', - 'internal_reference' => 'Внутренняя ссылка', - 'inward' => 'Внутреннее описание', - 'outward' => 'Внешнее описание', - 'rule_group_id' => 'Группа правил', + 'due_date' => 'Срок', + 'payment_date' => 'Дата платежа', + 'invoice_date' => 'Дата выставления счёта', + 'internal_reference' => 'Внутренняя ссылка', + 'inward' => 'Внутреннее описание', + 'outward' => 'Внешнее описание', + 'rule_group_id' => 'Группа правил', + 'transaction_description' => 'Описание транзакции', + 'first_date' => 'Первая дата', + 'transaction_type' => 'Тип транзакции', + 'repeat_until' => 'Повторять до тех пор, пока', + 'recurring_description' => 'Описание повторяющейся транзакции', + 'repetition_type' => 'Тип повторения', + 'foreign_currency_id' => 'Иностранная валюта', + 'repetition_end' => 'Заканчивать повторение', + 'repetitions' => 'Повторения', + 'calendar' => 'Календарь', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/ru_RU/import.php b/resources/lang/ru_RU/import.php index 457e18aed2..3f572ed187 100644 --- a/resources/lang/ru_RU/import.php +++ b/resources/lang/ru_RU/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', @@ -169,23 +172,23 @@ return [ // job configuration for file provider (stage: roles) 'job_config_roles_title' => 'Import setup (3/4) - Define each column\'s role', 'job_config_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.', - 'job_config_roles_submit' => 'Continue', - 'job_config_roles_column_name' => 'Name of column', + 'job_config_roles_submit' => 'Продолжить', + 'job_config_roles_column_name' => 'Название столбца', 'job_config_roles_column_example' => 'Column example data', 'job_config_roles_column_role' => 'Column data meaning', 'job_config_roles_do_map_value' => 'Map these values', 'job_config_roles_no_example' => 'No example data available', 'job_config_roles_fa_warning' => 'If you mark a column as containing an amount in a foreign currency, you must also set the column that contains which currency it is.', 'job_config_roles_rwarning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', - 'job_config_roles_colum_count' => 'Column', + 'job_config_roles_colum_count' => 'Столбец', // job config for the file provider (stage: mapping): 'job_config_map_title' => 'Import setup (4/4) - Connect import data to Firefly III data', 'job_config_map_text' => 'In the following tables, the left value shows you information found in your uploaded 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.', 'job_config_map_nothing' => 'There is no data present in your file that you can map to existing values. Please press "Start the import" to continue.', - 'job_config_field_value' => 'Field value', - 'job_config_field_mapped' => 'Mapped to', + 'job_config_field_value' => 'Значение поля', + 'job_config_field_mapped' => 'Сопоставлено с', 'map_do_not_map' => '(не сопоставлено)', - 'job_config_map_submit' => 'Start the import', + 'job_config_map_submit' => 'Начать импорт', // import status page: @@ -196,11 +199,11 @@ return [ 'status_job_running' => 'Please wait, running the import...', 'status_job_storing' => 'Please wait, storing data...', 'status_job_rules' => 'Please wait, running rules...', - 'status_fatal_title' => 'Fatal error', + 'status_fatal_title' => 'Фатальная ошибка', 'status_fatal_text' => 'The import has suffered from an error it could not recover from. Apologies!', 'status_fatal_more' => 'This (possibly very cryptic) error message is complemented by log files, which you can find on your hard drive, or in the Docker container where you run Firefly III from.', - 'status_finished_title' => 'Import finished', - 'status_finished_text' => 'The import has finished.', + 'status_finished_title' => 'Импорт завершён', + 'status_finished_text' => 'Импорт завершен!', 'finished_with_errors' => 'There were some errors during the import. Please review them carefully.', 'unknown_import_result' => 'Unknown import result', 'result_no_transactions' => 'No transactions have been imported. Perhaps they were all duplicates is simply no transactions where present to be imported. Perhaps the log files can tell you what happened. If you import data regularly, this is normal.', diff --git a/resources/lang/ru_RU/intro.php b/resources/lang/ru_RU/intro.php index 8f21f6457e..0aaae3dcaf 100644 --- a/resources/lang/ru_RU/intro.php +++ b/resources/lang/ru_RU/intro.php @@ -111,7 +111,7 @@ return [ 'bills_create_skip_holder' => 'Если счёт выставляется каждые 2 недели, в поле "пропустить" нужно поставить "1", чтобы пропускать все прочие недели.', // rules index - 'rules_index_intro' => 'Firefly III позволяет вам использовать правилами, которые автоматически применяются к любой транзакции, которую вы создаёте или редактируете.', + 'rules_index_intro' => 'Firefly III позволяет вам использовать правила, автоматически применяющиеся к любой транзакции, которую вы создаёте или редактируете.', 'rules_index_new_rule_group' => 'Вы можете комбинировать правила в группы, чтобы упростить управление ими.', 'rules_index_new_rule' => 'Создайте столько правил, сколько захотите.', 'rules_index_prio_buttons' => 'Упорядочивайте их так, как вы считаете нужным.', diff --git a/resources/lang/ru_RU/list.php b/resources/lang/ru_RU/list.php index ad3e77cc87..3d285ed97e 100644 --- a/resources/lang/ru_RU/list.php +++ b/resources/lang/ru_RU/list.php @@ -119,8 +119,13 @@ return [ 'file_type' => 'Тип файла', 'attached_to' => 'Прикреплено к', 'file_exists' => 'Файл существует', - 'spectre_bank' => 'Bank', - 'spectre_last_use' => 'Last login', - 'spectre_status' => 'Status', - 'bunq_payment_id' => 'bunq payment ID', + 'spectre_bank' => 'Банк', + 'spectre_last_use' => 'Последний вход', + 'spectre_status' => 'Статус', + 'bunq_payment_id' => 'ID платежа bunq', + 'repetitions' => 'Повторы', + 'title' => 'Название', + 'transaction_s' => 'Транзакции', + 'field' => 'Поле', + 'value' => 'Значение', ]; diff --git a/resources/lang/ru_RU/validation.php b/resources/lang/ru_RU/validation.php index 9d551d5344..e3fd87a974 100644 --- a/resources/lang/ru_RU/validation.php +++ b/resources/lang/ru_RU/validation.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ 'iban' => 'Это некорректный IBAN.', - 'source_equals_destination' => 'Счёт источник и счёт назначения совпадают', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Этот номер счёта уже используется.', 'unique_iban_for_user' => 'Этот IBAN уже используется.', 'deleted_user' => 'По соображениям безопасности, вы не можете зарегистрироваться, используя этот адрес электронной почты.', @@ -34,16 +34,22 @@ return [ 'file_attached' => 'Файл ":name". успешно загружен.', 'must_exist' => 'ID в поле field :attribute не существует в базе данных.', 'all_accounts_equal' => 'Все счета в данном поле должны совпадать.', - 'invalid_selection' => 'Вы сделали неправильный выбор', + 'invalid_selection' => 'Your selection is invalid.', 'belongs_user' => 'Данное значение недопустимо для этого поля.', 'at_least_one_transaction' => 'Необходима как минимум одна транзакция.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', 'require_currency_info' => 'Содержимое этого поля недействительно без информации о валюте.', 'equal_description' => 'Описание транзакции не должно совпадать с глобальным описанием.', 'file_invalid_mime' => 'Файл ":name" имеет тип ":mime". Загрузка файлов такого типа невозможна.', 'file_too_large' => 'Файл ":name" слишком большой.', - 'belongs_to_user' => 'Значение :attribute неизвестно', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => 'Необходимо принять :attribute.', 'bic' => 'Это некорректный BIC.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'This is not valid base64 encoded data.', + 'model_id_invalid' => 'The given ID seems invalid for this model.', 'more' => ':attribute должен быть больше нуля.', 'active_url' => ':attribute не является допустимым URL-адресом.', 'after' => ':attribute должна быть позже :date.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute должен быть массивом.', 'unique_for_user' => 'Уже существует запись с этим :attribute.', 'before' => ':attribute должна быть раньше :date.', - 'unique_object_for_user' => 'Это имя уже используется', - 'unique_account_for_user' => 'Имя аккаунта уже используется', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => ':attribute должен быть больше :min и меньше :max.', 'between.file' => ':attribute должен быть размером :min - :max килобайт.', 'between.string' => ':attribute должен содержать :min - :max символов.', @@ -85,6 +91,9 @@ return [ 'min.array' => 'Значение :attribute должно содержать не меньше :min элементов.', 'not_in' => 'Выбранный :attribute не верный.', 'numeric' => ':attribute должен быть числом.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => 'Формат :attribute некорректен.', 'required' => 'Поле :attribute является обязательным.', 'required_if' => 'Значение :attribute является обязательным, когда :other равное :value.', @@ -109,9 +118,12 @@ return [ 'file' => ':attribute должен быть файлом.', 'in_array' => 'Поле :attribute не существует в :other.', 'present' => 'Поле :attribute должно быть заполнено.', - 'amount_zero' => 'Общее количество не может быть равно нулю', - 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'amount_zero' => 'The total amount cannot be zero.', + 'unique_piggy_bank_for_user' => 'Название копилки должно быть уникальным.', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ 'email' => '"Адрес электронной почты"', 'description' => '"Описание"', diff --git a/resources/lang/tr_TR/components.php b/resources/lang/tr_TR/components.php index 6299088f8e..32e959ed1d 100644 --- a/resources/lang/tr_TR/components.php +++ b/resources/lang/tr_TR/components.php @@ -24,9 +24,9 @@ declare(strict_types=1); return [ // profile - 'personal_access_tokens' => 'Personal access tokens', + 'personal_access_tokens' => 'Kişisel Erişim Anahtarı', // bills: - 'not_expected_period' => 'Not expected this period', - 'not_or_not_yet' => 'Not (yet)', + 'not_expected_period' => 'Bu periyotta beklenmiyor', + 'not_or_not_yet' => 'Henüz değil', ]; diff --git a/resources/lang/tr_TR/config.php b/resources/lang/tr_TR/config.php index 4bc8b0175d..8e98d16bf4 100644 --- a/resources/lang/tr_TR/config.php +++ b/resources/lang/tr_TR/config.php @@ -23,20 +23,29 @@ declare(strict_types=1); return [ - 'html_language' => 'tr', - 'locale' => 'tr, Turkish, tr_TR, tr_TR.utf8, tr_TR.S.UTF-8', - 'month' => '%B %Y', - 'month_and_day' => '%e %B %Y', - 'date_time' => '%e %B %Y, @ %T', - 'specific_day' => '%e %B %Y', - 'week_in_year' => '%W. hafta, %Y', - 'year' => '%Y', - 'half_year' => '%B %Y', - 'month_js' => 'MMMM YYYY', - 'month_and_day_js' => 'MMMM Do, YYYY', - 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', - 'specific_day_js' => 'D MMMM YYYY', - 'week_in_year_js' => '[Week] w, YYYY', - 'year_js' => 'YYYY', - 'half_year_js' => 'Q YYYY', + 'html_language' => 'tr', + 'locale' => 'tr, Turkish, tr_TR, tr_TR.utf8, tr_TR.S.UTF-8', + 'month' => '%B %Y', + 'month_and_day' => '%e %B %Y', + 'month_and_date_day' => '%A %B %e, %Y', + 'month_and_day_no_year' => '%B %e', + 'date_time' => '%e %B %Y, @ %T', + 'specific_day' => '%e %B %Y', + 'week_in_year' => '%W. hafta, %Y', + 'year' => '%Y', + 'half_year' => '%B %Y', + 'month_js' => 'MMMM YYYY', + 'month_and_day_js' => 'MMMM Do, YYYY', + 'date_time_js' => 'MMMM Do, YYYY, @ HH:mm:ss', + 'specific_day_js' => 'D MMMM YYYY', + 'week_in_year_js' => '[Week] w, YYYY', + 'year_js' => 'YYYY', + 'half_year_js' => 'Q YYYY', + 'dow_1' => 'Pazartesi', + 'dow_2' => 'Salı', + 'dow_3' => 'Çarşamba', + 'dow_4' => 'Perşembe', + 'dow_5' => 'Cuma', + 'dow_6' => 'Cumartesi', + 'dow_7' => 'Pazar', ]; diff --git a/resources/lang/tr_TR/demo.php b/resources/lang/tr_TR/demo.php index f60723c7a3..9b4e96f0a2 100644 --- a/resources/lang/tr_TR/demo.php +++ b/resources/lang/tr_TR/demo.php @@ -28,10 +28,15 @@ return [ 'index' => 'Firefly III\'e hoş geldiniz! Bu sayfada, finansal durumunuzun özetini görebilirsiniz. Daha fazla bilgi için Hesapları → Varlık Hesaplarını ve tabii ki deBütçe ve Rapor sayfalarına göz atın. Ya da sadece bir göz gezdirin ve ne durumda olduğunuzu görün.', 'accounts-index' => 'Varlık hesapları kişisel banka hesaplarınızdır. Gider hesaplar, mağazalar ve arkadaşlar gibi para harcadığınız hesaplardır. Gelir hesapları, işiniz veya diğer gelir kaynakları gibi para aldığınız hesaplardır. Bu sayfada bunları düzenleyebilir veya silebilirsiniz.', 'budgets-index' => 'Bu sayfa bütçelerinize genel bir bakış sunmaktadır. Üstteki çubuk bütçelenebilecek miktarı gösterir. Bu sağdaki tutara tıklayarak herhangi bir dönem için özelleştirilebilir. Gerçekte harcadığınız tutar aşağıdaki barda gösterilir. Aşağıda, bütçe başına harcamalar ve bütçeniz için ne kadar para ayırdığınız gösterilir.', - 'reports-index-start' => 'Firefly III supports a number of types of reports. Read about them by clicking on the -icon in the top right corner.', + 'reports-index-start' => 'Firefly III, bir dizi rapor türünü desteklemektedir. Sağ üst köşedeki simgeyi tıklayarak onları okuyabilirsiniz.', 'reports-index-examples' => 'Şu örnekleri incelediğinizden emin olun: a aylık finansal genel bakış, a yıllık finansal genel bakış ve a bütçe genel bakışı.', 'currencies-index' => 'Firefly III, birden fazla para birimini destekliyor. Euro varsayılan olmasına rağmen, ABD Doları ve diğer birçok para birimine ayarlanabilir. Gördüğünüz gibi küçük bir para birimi seçeneği dedahil edilmiştir ancak isterseniz kendi para biriminizi ekleyebilirsiniz. Varsayılan para birimi değiştirilebilir ancak mevcut işlemlerin para birimi değiştirilemez: Firefly III, aynı anda birden çok para biriminin kullanılmasını destekler.', 'transactions-index' => 'Bu masraflar, mevduatlar ve transferler için özellikle yaratıcı değildir. Bunlar otomatik olarak oluşturuldu.', 'piggy-banks-index' => 'Gördüğünüz gibi, üç tane banka var. Her domuzcuk bankasındaki para miktarını değiştirmek için artı ve eksi düğmelerini kullanın. Her domuzcuk bankasının yönetimini görmek için domuzcuk\'un üzerine tıklayın.', - 'import-index' => 'Any CSV file can be imported into Firefly III. It also supports importing data from bunq and Spectre. Other banks and financial aggregators will be implemented in the future. As a demo-user however, you can only see the "fake"-provider in action. It will generate some random transactions to show you how the process works.', + 'import-index' => 'Herhangi bir CSV dosyası Firefly III\'e aktarılabilir. Ayrıca bunq ve Spectre\'den veri almayı da destekliyor. Diğer bankalar ve finansal toplayıcılar gelecekte uygulanacaktır. Ancak bir demo kullanıcısı olarak, yalnızca "fake" -provider etkinlikte görebilirsiniz. Sürecin nasıl çalıştığını göstermek için bazı rasgele işlemler üretecektir. +', + 'recurring-index' => ' +Lütfen bu özelliğin aktif geliştirme aşamasında olduğunu ve beklendiği gibi çalışmayabileceğini unutmayın.', + 'recurring-create' => ' +Lütfen bu özelliğin aktif geliştirme aşamasında olduğunu ve beklendiği gibi çalışmayabileceğini unutmayın.', ]; diff --git a/resources/lang/tr_TR/firefly.php b/resources/lang/tr_TR/firefly.php index 32747c7c17..e7e77d38ab 100644 --- a/resources/lang/tr_TR/firefly.php +++ b/resources/lang/tr_TR/firefly.php @@ -46,17 +46,18 @@ return [ 'Opening balance' => 'Açılış bakiyesi', 'create_new_stuff' => 'Yeni bir şey oluştur', 'new_withdrawal' => 'Yeni para çekme', - 'create_new_transaction' => 'Create new transaction', - 'go_to_asset_accounts' => 'View your asset accounts', - 'go_to_budgets' => 'Go to your budgets', - 'go_to_categories' => 'Go to your categories', - 'go_to_bills' => 'Go to your bills', - 'go_to_expense_accounts' => 'See your expense accounts', - 'go_to_revenue_accounts' => 'See your revenue accounts', - 'go_to_piggies' => 'Go to your piggy banks', + 'create_new_transaction' => 'Yeni işlem oluştur', + 'go_to_asset_accounts' => 'Varlık hesaplarınızı görüntüleyin +', + 'go_to_budgets' => 'Bütçelerine git', + 'go_to_categories' => 'Kategorilerinize gidin', + 'go_to_bills' => 'Faturalarına git', + 'go_to_expense_accounts' => 'Gider hesaplarınızı görün', + 'go_to_revenue_accounts' => 'Gelir hesaplarınızı görün', + 'go_to_piggies' => 'Kumbaranıza gidin', 'new_deposit' => 'Yeni Depozito', 'new_transfer' => 'Yeni transfer', - 'new_transfers' => 'New transfer', + 'new_transfers' => 'Yeni transfer', 'new_asset_account' => 'Yeni varlık hesabı', 'new_expense_account' => 'Yeni gider hesabı', 'new_revenue_account' => 'Yeni Gelir Hesabı', @@ -71,9 +72,9 @@ return [ 'flash_error_multiple' => 'Bir hata var|:count hata var', 'net_worth' => 'Net değer', 'route_has_no_help' => 'Bu rota için yardım yok.', - 'help_for_this_page' => 'Help for this page', - 'no_help_could_be_found' => 'No help text could be found.', - 'no_help_title' => 'Apologies, an error occurred.', + 'help_for_this_page' => 'Bu sayfa için yardım', + 'no_help_could_be_found' => 'Hiçbir yardım metni bulunamadı.', + 'no_help_title' => 'Üzgünüz, bir hata oluştu.', 'two_factor_welcome' => 'Merhaba, :user!', 'two_factor_enter_code' => 'Devam etmek için lütfen iki faktörlü kimlik doğrulama kodunuzu girin. Uygulamanız sizin için oluşturabilir.', 'two_factor_code_here' => 'Kodu buraya girin', @@ -122,14 +123,14 @@ return [ 'clone_deposit' => 'Bu depozitoyu klonla', 'clone_transfer' => 'Bu transferi kopyala', 'multi_select_no_selection' => 'Hiçbiri seçilmedi', - 'multi_select_select_all' => 'Select all', - 'multi_select_n_selected' => 'selected', + 'multi_select_select_all' => 'Tümünü seç', + 'multi_select_n_selected' => 'seçili', 'multi_select_all_selected' => 'Tümü seçildi', 'multi_select_filter_placeholder' => 'Bul..', - 'intro_next_label' => 'Next', - 'intro_prev_label' => 'Previous', - 'intro_skip_label' => 'Skip', - 'intro_done_label' => 'Done', + 'intro_next_label' => 'Sonraki', + 'intro_prev_label' => 'Önceki', + 'intro_skip_label' => 'Atla', + 'intro_done_label' => 'Bitti', 'between_dates_breadcrumb' => ':start ve :end arasında', 'all_journals_without_budget' => 'Bütçesiz tüm işlemler', 'journals_without_budget' => 'Bütçesiz İşlemler', @@ -161,42 +162,43 @@ return [ 'invalid_server_configuration' => 'Geçersiz sunucu yapılandırması', 'invalid_locale_settings' => 'Firefly III parasal tutarları biçimlendiremiyor çünkü gerekli paketler sunucunuzda yok. Bunun nasıl yapıldığıyla ilgili talimatlar var.', 'quickswitch' => 'Hızlı anahtar', - 'sign_in_to_start' => 'Sign in to start your session', - 'sign_in' => 'Sign in', - 'register_new_account' => 'Register a new account', - 'forgot_my_password' => 'I forgot my password', - 'problems_with_input' => 'There were some problems with your input.', - 'reset_password' => 'Reset your password', - 'button_reset_password' => 'Reset password', - 'reset_button' => 'Reset', - 'want_to_login' => 'I want to login', - 'button_register' => 'Register', - 'authorization' => 'Authorization', - 'active_bills_only' => 'active bills only', - 'average_per_bill' => 'average per bill', - 'expected_total' => 'expected total', + 'sign_in_to_start' => 'Oturumu başlatmak için giriş yapın', + 'sign_in' => 'Oturum aç', + 'register_new_account' => 'Yeni hesap kaydı', + 'forgot_my_password' => 'Şifremi unuttum', + 'problems_with_input' => 'Girişinizle ilgili bazı problemler var.', + 'reset_password' => 'Şifreni sıfırla', + 'button_reset_password' => 'Parolayı Sıfırla', + 'reset_button' => 'Sıfırla', + 'want_to_login' => 'Giriş yapmak istiyorum', + 'button_register' => 'Kayıt ol', + 'authorization' => 'Yetkilendirme', + 'active_bills_only' => 'sadece aktif faturalar', + 'average_per_bill' => 'fatura başına ortalama', + 'expected_total' => 'beklenen toplam', // API access - 'authorization_request' => 'Firefly III v:version Authorization Request', - 'authorization_request_intro' => ':client is requesting permission to access your financial administration. Would you like to authorize :client to access these records?', - 'scopes_will_be_able' => 'This application will be able to:', - 'button_authorize' => 'Authorize', - 'none_in_select_list' => '(none)', + 'authorization_request' => 'Firefly III v: version Yetkilendirme İsteği', + 'authorization_request_intro' => ':client finansal yönetiminize erişmek için izin istiyor. Bu kayıtlara erişmek için :client \'yi yetkilendirmek ister misiniz?', + 'scopes_will_be_able' => 'Bu uygulama şunları yapabilir:', + 'button_authorize' => 'İzin ver', + 'none_in_select_list' => '(Yok)', // check for updates: - 'update_check_title' => 'Check for updates', - 'admin_update_check_title' => 'Automatically check for update', - 'admin_update_check_explain' => 'Firefly III can check for updates automatically. When you enable this setting, it will contact Github to see if a new version of Firefly III is available. When it is, you will get a notification. You can test this notification using the button on the right. Please indicate below if you want Firefly III to check for updates.', - 'check_for_updates_permission' => 'Firefly III can check for updates, but it needs your permission to do so. Please go to the administration to indicate if you would like this feature to be enabled.', - 'updates_ask_me_later' => 'Ask me later', - 'updates_do_not_check' => 'Do not check for updates', - 'updates_enable_check' => 'Enable the check for updates', - 'admin_update_check_now_title' => 'Check for updates now', - 'admin_update_check_now_explain' => 'If you press the button, Firefly III will see if your current version is the latest.', - 'check_for_updates_button' => 'Check now!', - 'update_new_version_alert' => 'A new version of Firefly III is available. You are running v:your_version, the latest version is v:new_version which was released on :date.', - 'update_current_version_alert' => 'You are running v:version, which is the latest available release.', - 'update_newer_version_alert' => 'You are running v:your_version, which is newer than the latest release, v:new_version.', - 'update_check_error' => 'An error occurred while checking for updates. Please view the log files.', + 'update_check_title' => 'Güncellemeleri kontrol et', + 'admin_update_check_title' => 'Güncellemeleri otomatik olarak kontrol et', + 'admin_update_check_explain' => 'Firefly III güncellemeleri otomatik olarak kontrol edebilir. Bu ayarı etkinleştirdiğinizde, Firefly III\'ün yeni bir sürümünün mevcut olup olmadığını görmek için Github ile iletişime geçecektir. Bu olduğunda, bir bildirim alacaksınız. Bu bildirimi sağdaki düğmeyi kullanarak test edebilirsiniz. Firefly III\'ün güncellemeleri kontrol etmesini istiyorsanız lütfen aşağıya belirtin.', + 'check_for_updates_permission' => 'Firefly III güncellemeleri kontrol edebilir, ancak bunu yapmak için izniniz gerekir. Bu özelliğin etkinleştirilmesini isteyip istemediğinizi belirtmek için lütfen yönetim bölümüne gidin.', + 'updates_ask_me_later' => 'Daha sonra sor', + 'updates_do_not_check' => 'Güncelleştirmeleri kontrol ETME', + 'updates_enable_check' => 'Güncelleme kontrolünü etkinleştir', + 'admin_update_check_now_title' => 'Güncellemeleri kontrol et', + 'admin_update_check_now_explain' => 'Düğmeye basarsanız, Firefly III geçerli sürümünüzün en son olup olmadığını görür.', + 'check_for_updates_button' => 'Kontrol Et!', + 'update_new_version_alert' => 'Firefly III\'ün yeni bir sürümü mevcut. v:your_version çalıştırıyorsunuz, en son sürüm v:new_version :date. tarihinde yayınlandı.', + 'update_current_version_alert' => 'Mevcut en yeni sürümü v:version, kullanıyorsunuz.', + 'update_newer_version_alert' => 'En son sürüm olan v:new_version\'dan daha yeni olan v:your_version kullanıyorsunuz. +', + 'update_check_error' => 'Güncelleştirmeleri denetlerken bir hata oluştu. Lütfen günlük dosyalarını inceleyin.', // search 'search' => 'Ara', @@ -223,7 +225,7 @@ return [ // export data: 'import_and_export' => 'İçe al ve dışarı aktar', 'export_data' => 'Veriyi dışarı aktar', - 'export_and_backup_data' => 'Export data', + 'export_and_backup_data' => 'Veriyi dışarı aktar', 'export_data_intro' => 'Use the exported data to move to a new financial application. Please note that these files are not meant as a backup. They do not contain enough meta-data to fully restore a new Firefly III installation. If you want to make a backup of your data, please backup the database directly.', 'export_format' => 'Dışa Aktarma Biçimi', 'export_format_csv' => 'Virgülle ayrılmış değerler (CSV file)', @@ -269,8 +271,8 @@ return [ 'move_rule_group_down' => 'Grup kuralını aşağı taşı', 'save_rules_by_moving' => 'Bu kuralları başka bir kural grubuna taşıyarak kaydedin:', 'make_new_rule' => '":title" kural grubunda yeni kural oluşturun', - 'rule_is_strict' => 'strict rule', - 'rule_is_not_strict' => 'non-strict rule', + 'rule_is_strict' => 'sıkı kural', + 'rule_is_not_strict' => 'sıkı olmayan kural', 'rule_help_stop_processing' => 'Bu kutuyu işaretlediğinizde, bu grupta sonraki kurallar yürütülemez.', 'rule_help_strict' => 'In strict rules ALL triggers must fire for the action(s) to be executed. In non-strict rules, ANY trigger is enough for the action(s) to be executed.', 'rule_help_active' => 'Aktif olmayan kurallar asla çalışmaz.', @@ -412,7 +414,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'rule_action_clear_notes_choice' => 'Herhangi bir notu kaldır', 'rule_action_clear_notes' => 'Herhangi bir notu kaldır', 'rule_action_set_notes_choice' => 'Notları şuna ayarla..', - 'rule_action_link_to_bill_choice' => 'Link to a bill..', + 'rule_action_link_to_bill_choice' => 'Bir fatura bağlantı...', 'rule_action_link_to_bill' => 'Link to bill ":action_value"', 'rule_action_set_notes' => 'Notları ":action_value" olarak ayarla', @@ -466,6 +468,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'pref_two_factor_auth_code_help' => 'QR kodunu, telefonunuzda Authy veya Authenticator uygulamalarında bulun ve oluşturulan kodu girin.', 'pref_two_factor_auth_reset_code' => 'Doğrulama kodunu sıfırla', 'pref_two_factor_auth_disable_2fa' => 'Disable 2FA', + '2fa_use_secret_instead' => 'If you cannot scan the QR code, feel free to use the secret instead: :secret.', 'pref_save_settings' => 'Ayarları kaydet', 'saved_preferences' => 'Tercihler kaydedildi!', 'preferences_general' => 'Genel', @@ -669,7 +672,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'bill_will_automatch' => 'Fatura uygun işlemlere otomatik olarak bağlandı', 'skips_over' => 'atla', 'bill_store_error' => 'An unexpected error occurred while storing your new bill. Please check the log files', - 'list_inactive_rule' => 'inactive rule', + 'list_inactive_rule' => 'Etkin Olmayan Kurallar', // accounts: 'details_for_asset' => '":name" Varlık hesabı ayrıntıları', @@ -793,7 +796,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'no_bulk_category' => 'Don\'t update category', 'no_bulk_budget' => 'Don\'t update budget', 'no_bulk_tags' => 'Don\'t update tag(s)', - 'bulk_edit' => 'Bulk edit', + 'bulk_edit' => 'Toplu düzenle', 'cannot_edit_other_fields' => 'Gösterecek yer olmadığı için, bu dosya dışındaki dosyaları toplu olarak düzenleyemezsiniz. Eğer o alanları düzenlemeniz gerekliyse lütfen linki takip edin ve onları teker teker düzenleyin.', 'no_budget' => 'hiçbiri', 'no_budget_squared' => '(Bütçe yok)', @@ -805,12 +808,12 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'opt_group_savingAsset' => 'Tasarruf Hesapları', 'opt_group_sharedAsset' => 'Paylaşılan varlık hesapları', 'opt_group_ccAsset' => 'Kredi Kartı', - 'opt_group_cashWalletAsset' => 'Cash wallets', - 'notes' => 'Notes', + 'opt_group_cashWalletAsset' => 'Nakit cüzdan', + 'notes' => 'Notlar', 'unknown_journal_error' => 'Could not store the transaction. Please check the log files.', // new user: - 'welcome' => 'Welcome to Firefly III!', + 'welcome' => 'Firefly III\'e hoşgeldiniz!', 'submit' => 'Gönder', 'getting_started' => 'Başla', 'to_get_started' => 'Firefly III\'ü başarılı şekilde yüklediğinizi görmek güzel. Bu aracı kullanmak için lütfen banka adınızı ve ana hesabınızın bakiyesini girin. Birden fazla hesabınız varsa endişelenmeyin. Onları daha sonra ekleyebilirsiniz. Bu adım sadece Firefly III\'ün bir yerden başlaması gerektiği içindir.', @@ -818,10 +821,10 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'finish_up_new_user' => 'İşte bu! Submit tıklayarak devam edebilirsiniz. Firefly III Anasayfasına yönlendirileceksiniz.', 'stored_new_accounts_new_user' => 'Yuppi! Yeni hesabınız kaydedildi.', 'set_preferred_language' => 'If you prefer to use Firefly III in another language, please indicate so here.', - 'language' => 'Language', - 'new_savings_account' => ':bank_name savings account', - 'cash_wallet' => 'Cash wallet', - 'currency_not_present' => 'If the currency you normally use is not listed do not worry. You can create your own currencies under Options > Currencies.', + 'language' => 'Dil', + 'new_savings_account' => ':bank_name tasarruf hesabı', + 'cash_wallet' => 'Nakit cüzdan', + 'currency_not_present' => 'Normalde kullandığınız para birimi listelenmiyorsa endişelenmeyin. Seçenekler> Para Birimleri altında kendi para birimlerini oluşturabilirsiniz.', // home page: 'yourAccounts' => 'Hesaplarınız', @@ -901,7 +904,6 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'balanceEnd' => 'Dönem Sonunda Bakiye', 'splitByAccount' => 'Hesaplara göre bölünmüş', 'coveredWithTags' => 'Etiketler kapatılmıştır', - 'leftUnbalanced' => 'Sol dengesiz', 'leftInBudget' => 'Bütçede bırakıldı', 'sumOfSums' => 'Hesap toplamı', 'noCategory' => '(Kategori yok)', @@ -970,7 +972,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'account_role_sharedAsset' => 'Paylaşılan varlık hesabı', 'account_role_savingAsset' => 'Birikim hesabı', 'account_role_ccAsset' => 'Kredi Kartı', - 'account_role_cashWalletAsset' => 'Cash wallet', + 'account_role_cashWalletAsset' => 'Nakit cüzdan', 'budget_chart_click' => 'Bir grafik görmek için lütfen yukarıdaki tabloda bir bütçe adına tıklayın.', 'category_chart_click' => 'Bir grafik görmek için lütfen yukarıdaki tabloda bir kategori adına tıklayın.', 'in_out_accounts' => 'Kazanılan ve kombinasyon başına harcanan', @@ -1019,7 +1021,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'remove_money_from_piggy_title' => '":name" kumbarasından para çek', 'add' => 'Ekle', 'no_money_for_piggy' => 'Bu kumbaraya koyacak paran yok.', - 'suggested_savings_per_month' => 'Suggested per month', + 'suggested_savings_per_month' => 'Aylık önerildi', 'remove' => 'Kaldır', 'max_amount_add' => 'Ekleyebileceğiniz azami tutar', @@ -1031,7 +1033,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'events' => 'Etkinlikler', 'target_amount' => 'Hedef miktar', 'start_date' => 'Başlangıç Tarihi', - 'no_start_date' => 'No start date', + 'no_start_date' => 'başlangıç tarihi yok', 'target_date' => 'Hedeflenen tarih', 'no_target_date' => 'Hedef tarihi yok', 'table' => 'Tablo', @@ -1118,7 +1120,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'invalid_link_selection' => 'Bu işlemler bağlantılanamıyor', 'journals_linked' => 'İşlemler bağlantıları oluşturuldu.', 'journals_error_linked' => 'Bu işlemler zaten bağlantılı.', - 'journals_link_to_self' => 'You cannot link a transaction to itself', + 'journals_link_to_self' => 'Kendisi için bir ilişki ekleyemezsiniz.', 'journal_links' => 'İşlem bağlantıları', 'this_withdrawal' => 'Bu çekim', 'this_deposit' => 'Bu depozito', @@ -1135,6 +1137,8 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'is (partially) refunded by_inward' => 'tarafından (kısmen) geri ödendi', 'is (partially) paid for by_inward' => 'tarafından (kısmen) ödendi', 'is (partially) reimbursed by_inward' => 'tarafından (kısmen) iade edildi', + 'inward_transaction' => 'Içe işlem', + 'outward_transaction' => 'Dış işlem', 'relates to_outward' => 'ile ilişkili', '(partially) refunds_outward' => '(kısmen) geri ödeme', '(partially) pays for_outward' => 'için (kısmen) öder', @@ -1158,6 +1162,7 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', // Import page (general strings only) 'import_index_title' => 'Firefly III\'e veri aktarma', 'import_data' => 'Veriyi içe aktar', + 'import_transactions' => 'İşlemleri içe aktarma', // sandstorm.io errors and messages: 'sandstorm_not_available' => 'Bir Sandstorm.io ortamında Firefly III kullanıyorsanız, bu işlev kullanılamaz.', @@ -1207,4 +1212,68 @@ işlemlerin kontrol edildiğini lütfen unutmayın.', 'no_bills_intro_default' => 'Henüz bir faturanız yok. Kira veya sigortanız gibi düzenli giderleri takip etmek için faturalar oluşturabilirsiniz.', 'no_bills_imperative_default' => 'Böyle düzenli faturalarınız var mı? Bir fatura oluşturun ve ödemelerinizi takip edin:', 'no_bills_create_default' => 'Fatura oluştur', + + // recurring transactions + 'recurrences' => 'Tekrar Eden İşlemler', + 'no_recurring_title_default' => 'Yinelenen bir işlem yapalım!', + 'no_recurring_intro_default' => 'Henüz yinelenen işleminiz yok. Firefly III\'ün sizin için otomatik olarak işlemler oluşturmasını sağlamak için bunları kullanabilirsiniz.', + 'no_recurring_imperative_default' => 'Bu oldukça gelişmiş bir özelliktir ve son derece kullanışlı olabilir. Devam etmeden önce, (?) - sağ üst köşedeki dokümanları okuduğunuzdan emin olun.', + 'no_recurring_create_default' => 'Yinelenen bir işlem oluştur', + 'make_new_recurring' => 'Yinelenen bir işlem oluştur', + 'recurring_daily' => 'Her gün', + 'recurring_weekly' => 'Every week on :weekday', + 'recurring_monthly' => 'Every month on the :dayOfMonth(st/nd/rd/th) day', + 'recurring_ndom' => 'Every month on the :dayOfMonth(st/nd/rd/th) :weekday', + 'recurring_yearly' => 'Every year on :date', + 'overview_for_recurrence' => 'Overview for recurring transaction ":title"', + 'warning_duplicates_repetitions' => 'In rare instances, dates appear twice in this list. This can happen when multiple repetitions collide. Firefly III will always generate one transaction per day.', + 'created_transactions' => 'İlişkili işlemler', + 'expected_Withdrawals' => 'Expected withdrawals', + 'expected_Deposits' => 'Expected deposits', + 'expected_Transfers' => 'Expected transfers', + 'created_Withdrawals' => 'Created withdrawals', + 'created_Deposits' => 'Created deposits', + 'created_Transfers' => 'Created transfers', + 'created_from_recurrence' => 'Created from recurring transaction ":title" (#:id)', + + 'recurring_meta_field_tags' => 'Etiketler', + 'recurring_meta_field_notes' => 'Notlar', + 'recurring_meta_field_bill_id' => 'Fatura', + 'recurring_meta_field_piggy_bank_id' => 'Kumbara', + 'create_new_recurrence' => 'Yinelenen bir işlem oluştur', + 'help_first_date' => 'Indicate the first expected recurrence. This must be in the future.', + 'help_first_date_no_past' => 'Indicate the first expected recurrence. Firefly III will not create transactions in the past.', + 'no_currency' => '(no currency)', + 'mandatory_for_recurring' => 'Mandatory recurrence information', + 'mandatory_for_transaction' => 'Mandatory transaction information', + 'optional_for_recurring' => 'Optional recurrence information', + 'optional_for_transaction' => 'Optional transaction information', + 'change_date_other_options' => 'Change the "first date" to see more options.', + 'mandatory_fields_for_tranaction' => 'The values here will end up in the transaction(s) being created', + 'click_for_calendar' => 'Click here for a calendar that shows you when the transaction would repeat.', + 'repeat_forever' => 'Repeat forever', + 'repeat_until_date' => 'Repeat until date', + 'repeat_times' => 'Repeat a number of times', + 'recurring_skips_one' => 'Every other', + 'recurring_skips_more' => 'Skips :count occurrences', + 'store_new_recurrence' => 'Store recurring transaction', + 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', + 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', + 'recurrence_is_inactive' => 'This recurring transaction is not active and will not generate new transactions.', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'new_recurring_transaction' => 'New recurring transaction', + 'help_weekend' => 'What should Firefly III do when the recurring transaction falls on a Saturday or Sunday?', + 'do_nothing' => 'Just create the transaction', + 'skip_transaction' => 'Skip the occurence', + 'jump_to_friday' => 'Create the transaction on the previous Friday instead', + 'jump_to_monday' => 'Create the transaction on the next Monday instead', + 'will_jump_friday' => 'Will be created on Friday instead of the weekends.', + 'will_jump_monday' => 'Will be created on Monday instead of the weekends.', + 'except_weekends' => 'Except weekends', + 'recurrence_deleted' => 'Recurring transaction ":title" deleted', ]; diff --git a/resources/lang/tr_TR/form.php b/resources/lang/tr_TR/form.php index ac3f7c5de7..afb1492e96 100644 --- a/resources/lang/tr_TR/form.php +++ b/resources/lang/tr_TR/form.php @@ -24,66 +24,66 @@ declare(strict_types=1); return [ // new user: - 'bank_name' => 'Banka adı', - 'bank_balance' => 'Bakiye', - 'savings_balance' => 'Tasarruf bakiyesi', - 'credit_card_limit' => 'Kredi kartı limiti', - 'automatch' => 'Otomatik olarak eşleştir', - 'skip' => 'Atla', - 'name' => 'İsim', - 'active' => 'Aktif', - 'amount_min' => 'Minimum tutar', - 'amount_max' => 'Minimum tutar', - 'match' => 'Eşleşti', - 'strict' => 'Strict mode', - 'repeat_freq' => 'Tekrarlar', - 'journal_currency_id' => 'Para birimi', - 'currency_id' => 'Para birimi', - 'transaction_currency_id' => 'Currency', - 'external_ip' => 'Your server\'s external IP', - 'attachments' => 'Ekler', - 'journal_amount' => 'Tutar', - 'journal_source_account_name' => 'Gelir hesabı (kaynak)', - 'journal_source_account_id' => 'Varlık hesabı (kaynak)', - 'BIC' => 'BIC', - 'verify_password' => 'Parola güvenliğini doğrula', - 'source_account' => 'Kaynak hesap', - 'destination_account' => 'Hedef Hesap', - 'journal_destination_account_id' => 'Öğe hesabı (Hedef)', - 'asset_destination_account' => 'Öğe hesabı (Hedef)', - 'asset_source_account' => 'Varlık Hesabı (kaynak)', - 'journal_description' => 'Tanımlama', - 'note' => 'Notlar', - 'split_journal' => 'Bu işlemi böl / Taksitlendir', - 'split_journal_explanation' => 'Bu işlemi taksitlendirin', - 'currency' => 'Para birimi', - 'account_id' => 'Varlık hesabı', - 'budget_id' => 'Bütçe', - 'openingBalance' => 'Açılış bakiyesi', - 'tagMode' => 'Etiket modu', - 'tag_position' => 'Etiket konumu', - 'virtualBalance' => 'Sanal bakiye', - 'targetamount' => 'Hedef tutar', - 'accountRole' => 'Hesap rolü', - 'openingBalanceDate' => 'Açılış bakiyesi tarihi', - 'ccType' => 'Kredi kartı ödeme planı', - 'ccMonthlyPaymentDate' => 'Kredi kartı aylık ödeme tarihi', - 'piggy_bank_id' => 'Kumbara', - 'returnHere' => 'Dön buraya', - 'returnHereExplanation' => 'Sakladıktan sonra, başka bir tane oluşturmak için buraya dön.', - 'returnHereUpdateExplanation' => 'Güncelledikten sonra buraya dönün.', - 'description' => 'Tanımlama', - 'expense_account' => 'Gider Hesabı', - 'revenue_account' => 'Gelir hesabı', - 'decimal_places' => 'Ondalık basamak', - 'exchange_rate_instruction' => 'Yabancı para birimleri', - 'source_amount' => 'Miktar (kaynak)', - 'destination_amount' => 'Miktar (Hedef)', - 'native_amount' => 'Yerli Miktar', - 'new_email_address' => 'Yeni e-posta adresi', - 'verification' => 'Doğrulama', - 'api_key' => 'API anahtarı', - 'remember_me' => 'Remember me', + 'bank_name' => 'Banka adı', + 'bank_balance' => 'Bakiye', + 'savings_balance' => 'Tasarruf bakiyesi', + 'credit_card_limit' => 'Kredi kartı limiti', + 'automatch' => 'Otomatik olarak eşleştir', + 'skip' => 'Atla', + 'name' => 'İsim', + 'active' => 'Aktif', + 'amount_min' => 'Minimum tutar', + 'amount_max' => 'Minimum tutar', + 'match' => 'Eşleşti', + 'strict' => 'Strict mode', + 'repeat_freq' => 'Tekrarlar', + 'journal_currency_id' => 'Para birimi', + 'currency_id' => 'Para birimi', + 'transaction_currency_id' => 'Currency', + 'external_ip' => 'Your server\'s external IP', + 'attachments' => 'Ekler', + 'journal_amount' => 'Tutar', + 'journal_source_name' => 'Revenue account (source)', + 'journal_source_id' => 'Asset account (source)', + 'BIC' => 'BIC', + 'verify_password' => 'Parola güvenliğini doğrula', + 'source_account' => 'Kaynak hesap', + 'destination_account' => 'Hedef Hesap', + 'journal_destination_id' => 'Asset account (destination)', + 'asset_destination_account' => 'Öğe hesabı (Hedef)', + 'asset_source_account' => 'Varlık Hesabı (kaynak)', + 'journal_description' => 'Tanımlama', + 'note' => 'Notlar', + 'split_journal' => 'Bu işlemi böl / Taksitlendir', + 'split_journal_explanation' => 'Bu işlemi taksitlendirin', + 'currency' => 'Para birimi', + 'account_id' => 'Varlık hesabı', + 'budget_id' => 'Bütçe', + 'openingBalance' => 'Açılış bakiyesi', + 'tagMode' => 'Etiket modu', + 'tag_position' => 'Etiket konumu', + 'virtualBalance' => 'Sanal bakiye', + 'targetamount' => 'Hedef tutar', + 'accountRole' => 'Hesap rolü', + 'openingBalanceDate' => 'Açılış bakiyesi tarihi', + 'ccType' => 'Kredi kartı ödeme planı', + 'ccMonthlyPaymentDate' => 'Kredi kartı aylık ödeme tarihi', + 'piggy_bank_id' => 'Kumbara', + 'returnHere' => 'Dön buraya', + 'returnHereExplanation' => 'Sakladıktan sonra, başka bir tane oluşturmak için buraya dön.', + 'returnHereUpdateExplanation' => 'Güncelledikten sonra buraya dönün.', + 'description' => 'Tanımlama', + 'expense_account' => 'Gider Hesabı', + 'revenue_account' => 'Gelir hesabı', + 'decimal_places' => 'Ondalık basamak', + 'exchange_rate_instruction' => 'Yabancı para birimleri', + 'source_amount' => 'Miktar (kaynak)', + 'destination_amount' => 'Miktar (Hedef)', + 'native_amount' => 'Yerli Miktar', + 'new_email_address' => 'Yeni e-posta adresi', + 'verification' => 'Doğrulama', + 'api_key' => 'API anahtarı', + 'remember_me' => 'Remember me', 'source_account_asset' => 'Kaynak Hesabı (varlık hesabı)', 'destination_account_expense' => 'Hedef Hesap (gider hesabı)', @@ -94,90 +94,93 @@ return [ 'convert_Deposit' => 'Mevduata dönüştürün', 'convert_Transfer' => 'Aktarımı dönüştür', - 'amount' => 'Tutar', - 'foreign_amount' => 'Foreign amount', - 'existing_attachments' => 'Existing attachments', - 'date' => 'Tarih', - 'interest_date' => 'Faiz tarihi', - 'book_date' => 'Kitap Tarihi', - 'process_date' => 'İşlem tarihi', - 'category' => 'Kategori', - 'tags' => 'Etiketler', - 'deletePermanently' => 'Kalıcı olarak sil', - 'cancel' => 'İptal', - 'targetdate' => 'Hedeflenen tarih', - 'startdate' => 'Başlangıç Tarihi', - 'tag' => 'Etiket', - 'under' => 'Altında', - 'symbol' => 'Sembol', - 'code' => 'Kod', - 'iban' => 'IBAN numarası', - 'accountNumber' => 'Hesap numarası', - 'creditCardNumber' => 'Kredi Kartı Numarası', - 'has_headers' => 'Başlıklar', - 'date_format' => 'Tarih formatı', - 'specifix' => 'Banka veya dosyalara özel düzeltmeler', - 'attachments[]' => 'Ekler', - 'store_new_withdrawal' => 'Yeni para çekme oluştur', - 'store_new_deposit' => 'Yeni depozito oluştur', - 'store_new_transfer' => 'Yeni transfer oluştur', - 'add_new_withdrawal' => 'Yeni para çekme ekle', - 'add_new_deposit' => 'Yeni depozito ekle', - 'add_new_transfer' => 'Yeni bir transfer ekle', - 'title' => 'Başlık', - 'notes' => 'Notlar', - 'filename' => 'Dosya adı', - 'mime' => 'MIME türü', - 'size' => 'Boyut', - 'trigger' => 'Tetikleyici', - 'stop_processing' => 'İşlemeyi durdur', - 'start_date' => 'Sayfa başlangıcı', - 'end_date' => 'Kapsama alanı dışında', - 'export_start_range' => 'İhracatın başlangıcı', - 'export_end_range' => 'İhracatın sonu', - 'export_format' => 'Dosya biçimi', - 'include_attachments' => 'Yüklenen ekleri dahil et', - 'include_old_uploads' => 'İçe aktarılan verileri dahil et', - 'accounts' => 'Bu hesaptan işlemleri çıkarın', - 'delete_account' => '":name" adlı hesabı sil', - 'delete_bill' => 'Faturayı sil ":name"', - 'delete_budget' => '":name" bütçesini sil', - 'delete_category' => '":name" kategorisini sil', - 'delete_currency' => 'Para birimini sil ":name"', - 'delete_journal' => '":description" açıklamalı işlemi sil', - 'delete_attachment' => '":name" eklentisini sil', - 'delete_rule' => '\'":title" kuralını sil', - 'delete_rule_group' => '":title" kural grubunu sil', - 'delete_link_type' => '":name" bağlantı türünü sil', - 'delete_user' => '":email" kullanıcısını sil', - 'user_areYouSure' => '":email" kullanıcısını silerseniz her şey gitmiş olacak. Geriye alma, silmeyi iptal etme veya başka bir şey yoktur. Eğer kendinizi silerseniz, bu Firefly III\'e erişiminizi kaybedersiniz.', - 'attachment_areYouSure' => '":name" isimli eklentiyi silmek istediğinizden emin misiniz?', - 'account_areYouSure' => '":name" isimli hesabı silmek istediğinizden emin misiniz?', - 'bill_areYouSure' => '":name" isimli faturayı silmek istediğinizden emin misiniz?', - 'rule_areYouSure' => '":title" başlıklı kuralı silmek istediğinizden emin misiniz?', - 'ruleGroup_areYouSure' => '":title" başlıklı kural grubunu silmek istediğinizden emin misiniz?', - 'budget_areYouSure' => '":name" isimli bütçeyi silmek istediğinizden emin misiniz?', - 'category_areYouSure' => '":name" isimli kategoriyi silmek istediğinizden emin misiniz?', - 'currency_areYouSure' => '":name" isimli para birimini silmek istediğinizden emin misiniz?', - 'piggyBank_areYouSure' => '":name" isimli kumbarayı silmek istediğinizden emin misiniz?', - 'journal_areYouSure' => ':description" açıklamalı işlemi silmek istediğinizden emin misiniz?', - 'mass_journal_are_you_sure' => 'Bu işlemi silmek istediğinizden emin misiniz?', - 'tag_areYouSure' => '":tag" etiketini silmek istediğinizden emin misiniz?', - 'journal_link_areYouSure' => ':source and :destination arasındaki bağlantıyı silmek istediğinizden emin misiniz?', - 'linkType_areYouSure' => '":name" (":inward" / ":outward") bağlantı türünü silmek istediğinizden emin misiniz?', - 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', - 'mass_make_selection' => 'Onay kutusunu kaldırarak öğelerin silinmesini engelleyebilirsiniz.', - 'delete_all_permanently' => 'Seçilenleri kalıcı olarak sil', - 'update_all_journals' => 'Bu işlemleri güncelleyin', - 'also_delete_transactions' => 'Bu hesaba bağlı olan tek işlem de silinecektir. |Bu hesaba bağlı tüm :count işlemleri de silinecektir.', - 'also_delete_connections' => 'Bu bağlantıya bağlı tek işlem bağlantısını kaybedecek.| Bu bağlantı türüne bağlı tüm :count işlemleri bağlantılarını kaybedecek.', - 'also_delete_rules' => 'Bu hesaba bağlı olan tek kural grubu da silinecektir. |Bu hesaba bağlı tüm :count kuralları da silinecektir.', - 'also_delete_piggyBanks' => 'Bu hesaba bağlı olan tek kumbara da silinecektir. |Bu hesaba bağlı olan tüm :count kumbaraları da silinecektir.', - 'bill_keep_transactions' => 'Bu hesaba bağlı olan tek işlem de silinmeyecek. |Bu hesaba bağlı tüm :count işlemleri de silinmeden muaf olacaklar.', - 'budget_keep_transactions' => 'Bu bütçeye bağlı olan tek işlem silinmeyecek. |Bu bütçeye bağlı tüm :count işlemleri de silinmeden muaf olacaklar.', - 'category_keep_transactions' => 'Bu kategoriye bağlı olan tek işlem silinmeyecek. |Bu kategoriye bağlı tüm :count işlemleri de silinmeden muaf olacaklar.', - 'tag_keep_transactions' => 'Bu etikete bağlı olan tek işlem silinmeyecek. |Bu etikete bağlı tüm :count işlemleri de silinmeden muaf olacaklar.', - 'check_for_updates' => 'Check for updates', + 'amount' => 'Tutar', + 'foreign_amount' => 'Foreign amount', + 'existing_attachments' => 'Existing attachments', + 'date' => 'Tarih', + 'interest_date' => 'Faiz tarihi', + 'book_date' => 'Kitap Tarihi', + 'process_date' => 'İşlem tarihi', + 'category' => 'Kategori', + 'tags' => 'Etiketler', + 'deletePermanently' => 'Kalıcı olarak sil', + 'cancel' => 'İptal', + 'targetdate' => 'Hedeflenen tarih', + 'startdate' => 'Başlangıç Tarihi', + 'tag' => 'Etiket', + 'under' => 'Altında', + 'symbol' => 'Sembol', + 'code' => 'Kod', + 'iban' => 'IBAN numarası', + 'accountNumber' => 'Hesap numarası', + 'creditCardNumber' => 'Kredi Kartı Numarası', + 'has_headers' => 'Başlıklar', + 'date_format' => 'Tarih formatı', + 'specifix' => 'Banka veya dosyalara özel düzeltmeler', + 'attachments[]' => 'Ekler', + 'store_new_withdrawal' => 'Yeni para çekme oluştur', + 'store_new_deposit' => 'Yeni depozito oluştur', + 'store_new_transfer' => 'Yeni transfer oluştur', + 'add_new_withdrawal' => 'Yeni para çekme ekle', + 'add_new_deposit' => 'Yeni depozito ekle', + 'add_new_transfer' => 'Yeni bir transfer ekle', + 'title' => 'Başlık', + 'notes' => 'Notlar', + 'filename' => 'Dosya adı', + 'mime' => 'MIME türü', + 'size' => 'Boyut', + 'trigger' => 'Tetikleyici', + 'stop_processing' => 'İşlemeyi durdur', + 'start_date' => 'Sayfa başlangıcı', + 'end_date' => 'Kapsama alanı dışında', + 'export_start_range' => 'İhracatın başlangıcı', + 'export_end_range' => 'İhracatın sonu', + 'export_format' => 'Dosya biçimi', + 'include_attachments' => 'Yüklenen ekleri dahil et', + 'include_old_uploads' => 'İçe aktarılan verileri dahil et', + 'accounts' => 'Bu hesaptan işlemleri çıkarın', + 'delete_account' => '":name" adlı hesabı sil', + 'delete_bill' => 'Faturayı sil ":name"', + 'delete_budget' => '":name" bütçesini sil', + 'delete_category' => '":name" kategorisini sil', + 'delete_currency' => 'Para birimini sil ":name"', + 'delete_journal' => '":description" açıklamalı işlemi sil', + 'delete_attachment' => '":name" eklentisini sil', + 'delete_rule' => '\'":title" kuralını sil', + 'delete_rule_group' => '":title" kural grubunu sil', + 'delete_link_type' => '":name" bağlantı türünü sil', + 'delete_user' => '":email" kullanıcısını sil', + 'delete_recurring' => 'Delete recurring transaction ":title"', + 'user_areYouSure' => '":email" kullanıcısını silerseniz her şey gitmiş olacak. Geriye alma, silmeyi iptal etme veya başka bir şey yoktur. Eğer kendinizi silerseniz, bu Firefly III\'e erişiminizi kaybedersiniz.', + 'attachment_areYouSure' => '":name" isimli eklentiyi silmek istediğinizden emin misiniz?', + 'account_areYouSure' => '":name" isimli hesabı silmek istediğinizden emin misiniz?', + 'bill_areYouSure' => '":name" isimli faturayı silmek istediğinizden emin misiniz?', + 'rule_areYouSure' => '":title" başlıklı kuralı silmek istediğinizden emin misiniz?', + 'ruleGroup_areYouSure' => '":title" başlıklı kural grubunu silmek istediğinizden emin misiniz?', + 'budget_areYouSure' => '":name" isimli bütçeyi silmek istediğinizden emin misiniz?', + 'category_areYouSure' => '":name" isimli kategoriyi silmek istediğinizden emin misiniz?', + 'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?', + 'currency_areYouSure' => '":name" isimli para birimini silmek istediğinizden emin misiniz?', + 'piggyBank_areYouSure' => '":name" isimli kumbarayı silmek istediğinizden emin misiniz?', + 'journal_areYouSure' => ':description" açıklamalı işlemi silmek istediğinizden emin misiniz?', + 'mass_journal_are_you_sure' => 'Bu işlemi silmek istediğinizden emin misiniz?', + 'tag_areYouSure' => '":tag" etiketini silmek istediğinizden emin misiniz?', + 'journal_link_areYouSure' => ':source and :destination arasındaki bağlantıyı silmek istediğinizden emin misiniz?', + 'linkType_areYouSure' => '":name" (":inward" / ":outward") bağlantı türünü silmek istediğinizden emin misiniz?', + 'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.', + 'mass_make_selection' => 'Onay kutusunu kaldırarak öğelerin silinmesini engelleyebilirsiniz.', + 'delete_all_permanently' => 'Seçilenleri kalıcı olarak sil', + 'update_all_journals' => 'Bu işlemleri güncelleyin', + 'also_delete_transactions' => 'Bu hesaba bağlı olan tek işlem de silinecektir. |Bu hesaba bağlı tüm :count işlemleri de silinecektir.', + 'also_delete_connections' => 'Bu bağlantıya bağlı tek işlem bağlantısını kaybedecek.| Bu bağlantı türüne bağlı tüm :count işlemleri bağlantılarını kaybedecek.', + 'also_delete_rules' => 'Bu hesaba bağlı olan tek kural grubu da silinecektir. |Bu hesaba bağlı tüm :count kuralları da silinecektir.', + 'also_delete_piggyBanks' => 'Bu hesaba bağlı olan tek kumbara da silinecektir. |Bu hesaba bağlı olan tüm :count kumbaraları da silinecektir.', + 'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.', + 'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.', + 'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.', + 'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.', + 'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.', + 'check_for_updates' => 'Check for updates', 'email' => 'E-posta adresi', 'password' => 'Şifre', @@ -216,11 +219,23 @@ return [ 'country_code' => 'Ülke kodu', 'provider_code' => 'Banka ya da veri sağlayıcı', - 'due_date' => 'Bitiş Tarihi', - 'payment_date' => 'Ödeme Tarihi', - 'invoice_date' => 'Fatura Tarihi', - 'internal_reference' => 'Dahili referans', - 'inward' => 'Dahili açıklama', - 'outward' => 'Harici açıklama', - 'rule_group_id' => 'Kural grubu', + 'due_date' => 'Bitiş Tarihi', + 'payment_date' => 'Ödeme Tarihi', + 'invoice_date' => 'Fatura Tarihi', + 'internal_reference' => 'Dahili referans', + 'inward' => 'Dahili açıklama', + 'outward' => 'Harici açıklama', + 'rule_group_id' => 'Kural grubu', + 'transaction_description' => 'Transaction description', + 'first_date' => 'First date', + 'transaction_type' => 'Transaction type', + 'repeat_until' => 'Repeat until', + 'recurring_description' => 'Recurring transaction description', + 'repetition_type' => 'Type of repetition', + 'foreign_currency_id' => 'Foreign currency', + 'repetition_end' => 'Repetition ends', + 'repetitions' => 'Repetitions', + 'calendar' => 'Calendar', + 'weekend' => 'Weekend', + ]; diff --git a/resources/lang/tr_TR/import.php b/resources/lang/tr_TR/import.php index dd1ecba61d..d5b413b0c1 100644 --- a/resources/lang/tr_TR/import.php +++ b/resources/lang/tr_TR/import.php @@ -127,13 +127,16 @@ return [ 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'imported_from_account' => 'Imported from ":account"', 'spectre_account_with_number' => 'Account :number', + 'job_config_spectre_apply_rules' => 'Apply rules', + 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // job configuration for bunq: 'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'should_download_config' => 'You should download the configuration file for this job. This will make future imports way easier.', 'share_config_file' => 'If you have imported data from a public bank, you should share your configuration file so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', - + 'job_config_bunq_apply_rules' => 'Apply rules', + 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', // keys from "extra" array: 'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_swift' => 'SWIFT', diff --git a/resources/lang/tr_TR/list.php b/resources/lang/tr_TR/list.php index 4b2516e5f6..100e0aa33a 100644 --- a/resources/lang/tr_TR/list.php +++ b/resources/lang/tr_TR/list.php @@ -34,7 +34,8 @@ return [ 'name' => 'İsim', 'role' => 'Rol', 'currentBalance' => 'Cari bakiye', - 'linked_to_rules' => 'Relevant rules', + 'linked_to_rules' => ' +İlgili kurallar', 'active' => 'Aktif mi?', 'lastActivity' => 'Son Etkinlik', 'balanceDiff' => 'Bakiye farkı', @@ -92,7 +93,7 @@ return [ 'budget_count' => 'Bütçelerin sayısı', 'rule_and_groups_count' => 'Kuralların ve kural gruplarının sayısı', 'tags_count' => 'Etiket sayısı', - 'tags' => 'Tags', + 'tags' => 'Etiketler', 'inward' => 'Dahili açıklama', 'outward' => 'Dışa açıklama', 'number_of_transactions' => 'İşlem sayısı', @@ -103,8 +104,8 @@ return [ 'sum_deposits' => 'Toplam para yatırma', 'sum_transfers' => 'Transferlerin toplamı', 'reconcile' => 'Onaylanmış', - 'account_on_spectre' => 'Account (Spectre)', - 'do_import' => 'Import from this account', + 'account_on_spectre' => '(Spectre) Hesabı', + 'do_import' => 'Bu hesaptan içeri aktar', 'sepa-ct-id' => 'SEPA End to End Identifier', 'sepa-ct-op' => 'SEPA Opposing Account Identifier', 'sepa-db' => 'SEPA Mandate Identifier', @@ -114,13 +115,18 @@ return [ 'sepa-ci' => 'SEPA Creditor Identifier', 'external_id' => 'External ID', 'account_at_bunq' => 'Account with bunq', - 'file_name' => 'File name', - 'file_size' => 'File size', - 'file_type' => 'File type', - 'attached_to' => 'Attached to', - 'file_exists' => 'File exists', - 'spectre_bank' => 'Bank', - 'spectre_last_use' => 'Last login', - 'spectre_status' => 'Status', - 'bunq_payment_id' => 'bunq payment ID', + 'file_name' => 'Dosya adı', + 'file_size' => 'Dosya boyutu', + 'file_type' => 'Dosya tipi', + 'attached_to' => 'Şuraya Bağlı', + 'file_exists' => 'Dosya var', + 'spectre_bank' => 'Banka', + 'spectre_last_use' => 'Son giriş', + 'spectre_status' => 'Durum', + 'bunq_payment_id' => 'bunq ödeme kimliği', + 'repetitions' => 'Tekrarlar', + 'title' => 'Başlık', + 'transaction_s' => 'İşlemler', + 'field' => 'Alan', + 'value' => 'Değer', ]; diff --git a/resources/lang/tr_TR/validation.php b/resources/lang/tr_TR/validation.php index 7a4374c4d5..9fa4b2ad3b 100644 --- a/resources/lang/tr_TR/validation.php +++ b/resources/lang/tr_TR/validation.php @@ -23,27 +23,33 @@ declare(strict_types=1); return [ - 'iban' => 'Bu IBAN geçerli değilrdir.', - 'source_equals_destination' => 'The source account equals the destination account', + 'iban' => 'Bu geçerli bir IBAN değil.', + 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'Bu hesap numarası zaten kullanılmaktadır.', - 'unique_iban_for_user' => 'It looks like this IBAN is already in use.', + 'unique_iban_for_user' => 'Bu IBAN numarası zaten kullanılmaktadır.', 'deleted_user' => 'Güvenlik kısıtlamaları nedeniyle, bu e-posta adresini kullanarak kayıt yapamazsınız.', 'rule_trigger_value' => 'Bu eylem, seçili işlem için geçersizdir.', 'rule_action_value' => 'Bu eylem seçili işlem için geçersizdir.', 'file_already_attached' => 'Yüklenen dosya ":name" zaten bu nesneye bağlı.', 'file_attached' => '":name" dosyası başarıyla yüklendi.', - 'must_exist' => 'The ID in field :attribute does not exist in the database.', - 'all_accounts_equal' => 'All accounts in this field must be equal.', - 'invalid_selection' => 'Your selection is invalid', - 'belongs_user' => 'This value is invalid for this field.', - 'at_least_one_transaction' => 'Need at least one transaction.', - 'require_currency_info' => 'The content of this field is invalid without currency information.', - 'equal_description' => 'Transaction description should not equal global description.', + 'must_exist' => 'ID alanı :attribute veritabanın içinde yok.', + 'all_accounts_equal' => 'Bu alandaki tüm hesapları eşit olmalıdır.', + 'invalid_selection' => 'Your selection is invalid.', + 'belongs_user' => 'Bu değer bu alan için geçerli değil.', + 'at_least_one_transaction' => 'En az bir işlem gerekir.', + 'at_least_one_repetition' => 'Need at least one repetition.', + 'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.', + 'require_currency_info' => 'Bu alanın içeriği para birimi bilgileri geçersiz.', + 'equal_description' => 'İşlem açıklaması genel açıklama eşit değildir.', 'file_invalid_mime' => '":name" dosyası ":mime" türünde olup yeni bir yükleme olarak kabul edilemez.', 'file_too_large' => '":name" dosyası çok büyük.', - 'belongs_to_user' => ':attribute\'nin değeri bilinmiyor', + 'belongs_to_user' => 'The value of :attribute is unknown.', 'accepted' => ':attribute kabul edilmek zorunda.', 'bic' => 'Bu BIC geçerli değilrdir.', + 'at_least_one_trigger' => 'Rule must have at least one trigger.', + 'at_least_one_action' => 'Rule must have at least one action.', + 'base64' => 'Bu geçerli Base64 olarak kodlanmış veri değildir.', + 'model_id_invalid' => 'Verilen kimlik bu model için geçersiz görünüyor.', 'more' => ':attribute sıfırdan büyük olmak zorundadır.', 'active_url' => ':attribute geçerli bir URL değil.', 'after' => ':attribute :date tarihinden sonrası için tarihlendirilmelidir.', @@ -53,8 +59,8 @@ return [ 'array' => ':attribute bir dizi olmalıdır.', 'unique_for_user' => ':attribute\'de zaten bir girdi var.', 'before' => ':attribute :date tarihinden öncesi için tarihlendirilmelidir.', - 'unique_object_for_user' => 'Bu isim zaten kullanılıyor', - 'unique_account_for_user' => 'Bu hesap adı zaten kullanılıyor', + 'unique_object_for_user' => 'This name is already in use.', + 'unique_account_for_user' => 'This account name is already in use.', 'between.numeric' => ':attribute :min ve :max arasında olmalıdır.', 'between.file' => ':attribute, :min kilobayt ve :max kilobayt arasında olmalıdır.', 'between.string' => ':attribute :min karakter ve :max karakter olmalıdır.', @@ -85,6 +91,9 @@ return [ 'min.array' => ':attribute en az :min öğe içermelidir.', 'not_in' => 'Seçili :attribute geçersiz.', 'numeric' => ':attribute sayı olmalıdır.', + 'numeric_native' => 'The native amount must be a number.', + 'numeric_destination' => 'The destination amount must be a number.', + 'numeric_source' => 'The source amount must be a number.', 'regex' => ':attribute biçimi geçersiz.', 'required' => ':attribute alanı gereklidir.', 'required_if' => ':other :value iken :attribute alanı gereklidir.', @@ -95,7 +104,7 @@ return [ 'required_without_all' => 'Hiçbir :values mevcut değilken :attribute alanı gereklidir.', 'same' => ':attribute ve :other eşleşmelidir.', 'size.numeric' => ':attribute :size olmalıdır.', - 'amount_min_over_max' => 'The minimum amount cannot be larger than the maximum amount.', + 'amount_min_over_max' => 'En az tutar en fazla tutardan büyük olamaz.', 'size.file' => ':attribute :size kilobyte olmalıdır.', 'size.string' => ':attribute :size karakter olmalıdır.', 'size.array' => ':attribute :size öğeye sahip olmalıdır.', @@ -109,43 +118,46 @@ return [ 'file' => ':attribute bir dosya olmalıdır.', 'in_array' => ':attribute alanı :other içinde olamaz.', 'present' => ':attribute alanı mevcut olmalıdır.', - 'amount_zero' => 'Toplam tutar sıfır olamaz', - 'unique_piggy_bank_for_user' => 'The name of the piggy bank must be unique.', - 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security', + 'amount_zero' => 'The total amount cannot be zero.', + 'unique_piggy_bank_for_user' => 'Kumbara adı benzersiz olmalıdır.', + 'secure_password' => 'This is not a secure password. Please try again. For more information, visit http://bit.ly/FF3-password-security.', + 'valid_recurrence_rep_type' => 'Invalid repetition type for recurring transactions.', + 'valid_recurrence_rep_moment' => 'Invalid repetition moment for this type of repetition.', + 'invalid_account_info' => 'Invalid account information.', 'attributes' => [ - 'email' => 'email address', - 'description' => 'description', - 'amount' => 'amount', - 'name' => 'name', - 'piggy_bank_id' => 'piggy bank ID', - 'targetamount' => 'target amount', - 'openingBalanceDate' => 'opening balance date', - 'openingBalance' => 'opening balance', - 'match' => 'match', - 'amount_min' => 'minimum amount', - 'amount_max' => 'maximum amount', - 'title' => 'title', - 'tag' => 'tag', - 'transaction_description' => 'transaction description', - 'rule-action-value.1' => 'rule action value #1', - 'rule-action-value.2' => 'rule action value #2', - 'rule-action-value.3' => 'rule action value #3', - 'rule-action-value.4' => 'rule action value #4', - 'rule-action-value.5' => 'rule action value #5', - 'rule-action.1' => 'rule action #1', - 'rule-action.2' => 'rule action #2', - 'rule-action.3' => 'rule action #3', - 'rule-action.4' => 'rule action #4', - 'rule-action.5' => 'rule action #5', - 'rule-trigger-value.1' => 'rule trigger value #1', - 'rule-trigger-value.2' => 'rule trigger value #2', - 'rule-trigger-value.3' => 'rule trigger value #3', - 'rule-trigger-value.4' => 'rule trigger value #4', - 'rule-trigger-value.5' => 'rule trigger value #5', - 'rule-trigger.1' => 'rule trigger #1', - 'rule-trigger.2' => 'rule trigger #2', - 'rule-trigger.3' => 'rule trigger #3', - 'rule-trigger.4' => 'rule trigger #4', - 'rule-trigger.5' => 'rule trigger #5', + 'email' => 'E-posta adresi', + 'description' => 'Açıklama', + 'amount' => 'Tutar', + 'name' => 'adı', + 'piggy_bank_id' => 'Kumbara ID', + 'targetamount' => 'Hedef tutar', + 'openingBalanceDate' => 'Açılış bakiyesi', + 'openingBalance' => 'Açılış bakiyesi', + 'match' => 'Eşleşme', + 'amount_min' => 'Minimum tutar', + 'amount_max' => 'Maksimum tutar', + 'title' => 'başlık', + 'tag' => 'etiket', + 'transaction_description' => 'İşlem Açıklaması', + 'rule-action-value.1' => 'kural eylemi değer #1', + 'rule-action-value.2' => 'kural eylemi değer #1', + 'rule-action-value.3' => 'kural eylem değeri #3', + 'rule-action-value.4' => 'kural eylem değeri #4', + 'rule-action-value.5' => 'kural eylem değeri #5', + 'rule-action.1' => 'kural eylemi #1', + 'rule-action.2' => 'kural eylemi #2', + 'rule-action.3' => 'kural eylemi #3', + 'rule-action.4' => 'kural eylemi #4', + 'rule-action.5' => 'kural eylemi #5', + 'rule-trigger-value.1' => 'kural tetikleyici değeri #1', + 'rule-trigger-value.2' => 'kural tetikleyici değeri #2', + 'rule-trigger-value.3' => 'kural tetikleyici değeri #3', + 'rule-trigger-value.4' => 'kural tetikleyici değeri #4', + 'rule-trigger-value.5' => 'kural tetikleyici değeri #5', + 'rule-trigger.1' => 'kural tetikleyici #1', + 'rule-trigger.2' => 'kural tetikleyici #2', + 'rule-trigger.3' => 'kural tetikleyici #3', + 'rule-trigger.4' => 'kural tetikleyici #4', + 'rule-trigger.5' => 'kural tetikleyici #5', ], ]; diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index feaba02711..827f0f4cc9 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -48,7 +48,9 @@ {% endif %} {{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }} - {{ ExpandedForm.checkbox('active','1', preFilled.active) }} + + {# only correct way to do active checkbox #} + {{ ExpandedForm.checkbox('active', 1) }}
diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 861cbce7b3..0f1274cb2d 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -14,7 +14,9 @@
diff --git a/resources/views/admin/link/show.twig b/resources/views/admin/link/show.twig index 3d452df573..b9639b981c 100644 --- a/resources/views/admin/link/show.twig +++ b/resources/views/admin/link/show.twig @@ -15,10 +15,10 @@   - {{ trans('firefly.source_transaction') }} + {{ trans('firefly.inward_transaction') }}   {{ trans('firefly.link_description') }} - {{ trans('firefly.destination_transaction') }} + {{ trans('firefly.outward_transaction') }}   diff --git a/resources/views/bills/create.twig b/resources/views/bills/create.twig index 9bfde12466..2548c0ed6b 100644 --- a/resources/views/bills/create.twig +++ b/resources/views/bills/create.twig @@ -35,7 +35,6 @@ {{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }} {{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }} {{ ExpandedForm.integer('skip',0) }} - {{ ExpandedForm.checkbox('active',1,true) }}
diff --git a/resources/views/bills/edit.twig b/resources/views/bills/edit.twig index 6302b36198..e45f891c5b 100644 --- a/resources/views/bills/edit.twig +++ b/resources/views/bills/edit.twig @@ -37,7 +37,8 @@ {{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }} {{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }} {{ ExpandedForm.integer('skip') }} - {{ ExpandedForm.checkbox('active',1) }} + {# only correct way to do active checkbox #} + {{ ExpandedForm.checkbox('active', 1) }}
diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index c912bc5583..f22355adb5 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -66,7 +66,7 @@
@@ -91,13 +91,13 @@ {{ 'rescan_old'|_ }}
- {% if object.data.notes != '' %} + {% if object.data.notes.data[0] %}

{{ 'notes'|_ }}

- {{ object.data.notes }} + {{ object.data.notes.data[0].markdown|raw }}
{% endif %} diff --git a/resources/views/budgets/edit.twig b/resources/views/budgets/edit.twig index eeaf34244e..4c2f03c6be 100644 --- a/resources/views/budgets/edit.twig +++ b/resources/views/budgets/edit.twig @@ -14,7 +14,8 @@

{{ 'mandatoryFields'|_ }}

- {{ ExpandedForm.checkbox('active') }} + {# only correct way to do active checkbox #} + {{ ExpandedForm.checkbox('active', 1) }} {{ ExpandedForm.text('name') }}
diff --git a/resources/views/demo/recurring/index.twig b/resources/views/demo/recurring/index.twig new file mode 100644 index 0000000000..366736f634 --- /dev/null +++ b/resources/views/demo/recurring/index.twig @@ -0,0 +1 @@ +{{ trans('demo.recurring-index') }} diff --git a/resources/views/demo/recurring/recurring-create.twig b/resources/views/demo/recurring/recurring-create.twig new file mode 100644 index 0000000000..f97b5249ed --- /dev/null +++ b/resources/views/demo/recurring/recurring-create.twig @@ -0,0 +1 @@ +{{ trans('demo.recurring-create') }} diff --git a/resources/views/emails/report-new-journals-html.twig b/resources/views/emails/report-new-journals-html.twig new file mode 100644 index 0000000000..a3022d0775 --- /dev/null +++ b/resources/views/emails/report-new-journals-html.twig @@ -0,0 +1,34 @@ +{% include 'emails.header-html' %} +

+ {% if journals.count == 1 %} + Firefly III has created a transaction for you. + {% endif %} + {% if journals.count > 1 %} + Firefly III has created {{ journals.count }} transactions for you. + {% endif %} +

+ + +{% if journals.count == 1 %} +

+ You can find it in your Firefly III installation:
+ {% for journal in journals %} + {{ journal.description }} ({{ journal|journalTotalAmount }}) + {% endfor %} +

+{% endif %} + +{% if journals.count > 1 %} +

+ You can find them in your Firefly III installation: +

+ +{% endif %} + +{% include 'emails.footer-html' %} diff --git a/resources/views/emails/report-new-journals-text.twig b/resources/views/emails/report-new-journals-text.twig new file mode 100644 index 0000000000..81faa232d5 --- /dev/null +++ b/resources/views/emails/report-new-journals-text.twig @@ -0,0 +1,25 @@ +{% include 'emails.header-text' %} +{% if journals.count == 1 %} +Firefly III has created a transaction for you. + +{% endif %} +{% if journals.count > 1 %} +Firefly III has created {{ journals.count }} transactions for you. +{% endif %} +{% if journals.count == 1 %} +You can find in in your Firefly III installation: + +{% for journal in journals %} +{{ journal.description }}: {{ route('transactions.show', journal.id) }} ({{ journal|journalTotalAmountPlain }}) +{% endfor %} +{% endif %} + +{% if journals.count > 1 %} +You can find them in your Firefly III installation: + +{% for journal in journals %} +- {{ journal.description }}: {{ route('transactions.show', journal.id) }} ({{ journal|journalTotalAmountPlain }}) +{% endfor %} +{% endif %} + +{% include 'emails.footer-text' %} diff --git a/resources/views/form/amount.twig b/resources/views/form/amount.twig index c28227d1f7..652f355148 100644 --- a/resources/views/form/amount.twig +++ b/resources/views/form/amount.twig @@ -28,6 +28,6 @@ {% include 'form/feedback' %}
- - + +
diff --git a/resources/views/form/options.twig b/resources/views/form/options.twig index 1734d8fe06..f11e49bbe9 100644 --- a/resources/views/form/options.twig +++ b/resources/views/form/options.twig @@ -24,7 +24,7 @@
diff --git a/resources/views/import/bunq/choose-accounts.twig b/resources/views/import/bunq/choose-accounts.twig index f5b4afba79..566837af83 100644 --- a/resources/views/import/bunq/choose-accounts.twig +++ b/resources/views/import/bunq/choose-accounts.twig @@ -7,6 +7,26 @@
+ +
+
+
+

{{ trans('import.job_config_bunq_apply_rules') }}

+
+
+
+
+

+ {{ trans('import.job_config_bunq_apply_rules_text') }} +

+ {{ ExpandedForm.checkbox('apply_rules', 1, true) }} +
+
+ +
+
+
+
diff --git a/resources/views/import/file/configure-upload.twig b/resources/views/import/file/configure-upload.twig index 59de0c8a74..4e1e31c46c 100644 --- a/resources/views/import/file/configure-upload.twig +++ b/resources/views/import/file/configure-upload.twig @@ -38,7 +38,7 @@ {{ ExpandedForm.checkbox('has_headers',1,importJob.configuration['has-headers'],{helpText: trans('import.job_config_uc_header_help')}) }} {{ ExpandedForm.text('date_format',importJob.configuration['date-format'],{helpText: trans('import.job_config_uc_date_help', {dateExample: phpdate('Ymd')}) }) }} {{ ExpandedForm.select('csv_delimiter', data.delimiters, importJob.configuration['delimiter'], {helpText: trans('import.job_config_uc_delimiter_help') } ) }} - {{ ExpandedForm.assetAccountList('csv_import_account', importJob.configuration['import-account'], {helpText: trans('import.job_config_uc_account_help')}) }} + {{ ExpandedForm.activeAssetAccountList('csv_import_account', importJob.configuration['import-account'], {helpText: trans('import.job_config_uc_account_help')}) }}

{{ 'optionalFields'|_ }}

diff --git a/resources/views/import/spectre/accounts.twig b/resources/views/import/spectre/accounts.twig index c6c0eedf82..4fbf0ba248 100644 --- a/resources/views/import/spectre/accounts.twig +++ b/resources/views/import/spectre/accounts.twig @@ -7,6 +7,26 @@
+ +
+
+
+

{{ trans('import.job_config_spectre_apply_rules') }}

+
+
+
+
+

+ {{ trans('import.job_config_spectre_apply_rules_text') }} +

+ {{ ExpandedForm.checkbox('apply_rules', 1, true) }} +
+
+ +
+
+
+
diff --git a/resources/views/list/piggy-bank-events.twig b/resources/views/list/piggy-bank-events.twig index 43c815b54a..d5f76e195b 100644 --- a/resources/views/list/piggy-bank-events.twig +++ b/resources/views/list/piggy-bank-events.twig @@ -23,10 +23,11 @@ + {% if event.amount < 0 %} - {{ trans('firefly.removed_amount', {amount: (event.amount)|formatAmountPlain})|raw }} + {{ trans('firefly.removed_amount', {amount: formatAmountByAccount(event.piggyBank.account, event.amount, false)})|raw }} {% else %} - {{ trans('firefly.added_amount', {amount: (event.amount)|formatAmountPlain})|raw }} + {{ trans('firefly.added_amount', {amount: formatAmountByAccount(event.piggyBank.account, event.amount, false)})|raw }} {% endif %} diff --git a/resources/views/partials/boxes.twig b/resources/views/partials/boxes.twig index 68708cbc25..95fc6ea5d8 100644 --- a/resources/views/partials/boxes.twig +++ b/resources/views/partials/boxes.twig @@ -1,7 +1,6 @@ - @@ -86,8 +86,7 @@ {{ ExpandedForm.text('tags') }} {# RELATE THIS TRANSFER TO A PIGGY BANK #} - {{ ExpandedForm.select('piggy_bank_id', piggies, '0') }} - + {{ ExpandedForm.piggyBankList('piggy_bank_id',0) }}
@@ -196,7 +195,7 @@ {{ ExpandedForm.optionsList('create','transaction') }}
@@ -211,7 +210,6 @@ {% block scripts %}