Merge branch 'release/4.6.12'

This commit is contained in:
James Cole 2017-12-29 20:06:56 +01:00
commit 59d732cba7
989 changed files with 20722 additions and 6165 deletions

View File

@ -3,7 +3,7 @@ APP_DEBUG=false
APP_NAME=FireflyIII APP_NAME=FireflyIII
APP_KEY=SomeRandomStringOf32CharsExactly APP_KEY=SomeRandomStringOf32CharsExactly
APP_LOG=daily APP_LOG=daily
APP_LOG_LEVEL=warning APP_LOG_LEVEL=notice
APP_URL=http://localhost APP_URL=http://localhost
TRUSTED_PROXIES= TRUSTED_PROXIES=

View File

@ -4,7 +4,7 @@
## Feature requests ## Feature requests
I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.org/requested-features/).
## Pull requests ## Pull requests

View File

@ -8,4 +8,4 @@ I am running Firefly III version x.x.x
#### Other important details (log files, system info): #### Other important details (log files, system info):
Please visit the /debug page to get extra debug information. Please click the version number in the right corner of any Firefly III page to get debug information.

4
.github/SUPPORT.md vendored
View File

@ -4,8 +4,8 @@
## Bugs ## Bugs
First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority! First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority!
## Installation problems ## Installation problems
Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can. Please take the time to read the [installation guide FAQ](https://firefly-iii.org/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can.

View File

@ -1,5 +1,24 @@
# 4.6.1.1
# 4.6.12
- Support for Indonesian.
- New report, see [issue 384](https://github.com/firefly-iii/firefly-iii/issues/384)
- [Issue 964](https://github.com/firefly-iii/firefly-iii/issues/964) as suggested by [gavu](https://github.com/gavu)
- Greatly improved Docker support and documentation.
- [Issue 1046](https://github.com/firefly-iii/firefly-iii/issues/1046), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1047](https://github.com/firefly-iii/firefly-iii/issues/1047), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1048](https://github.com/firefly-iii/firefly-iii/issues/1048), as reported by [webence](https://github.com/webence)
- [Issue 1049](https://github.com/firefly-iii/firefly-iii/issues/1049), as reported by [nicoschreiner](https://github.com/nicoschreiner)
- [Issue 1015](https://github.com/firefly-iii/firefly-iii/issues/1015), as reporterd by a user on Tweakers.net
- [Issue 1056](https://github.com/firefly-iii/firefly-iii/issues/1056), as reported by [repercussion](https://github.com/repercussion)
- [Issue 1061](https://github.com/firefly-iii/firefly-iii/issues/1061), as reported by [Meizikyn](https://github.com/Meizikyn)
- [Issue 1045](https://github.com/firefly-iii/firefly-iii/issues/1045), as reported by [gavu](https://github.com/gavu)
- First code for [issue 1040](https://github.com/firefly-iii/firefly-iii/issues/1040) ([simonsmiley](https://github.com/simonsmiley))
- [Issue 1059](https://github.com/firefly-iii/firefly-iii/issues/1059), as reported by [4oo4](https://github.com/4oo4)
- [Issue 1063](https://github.com/firefly-iii/firefly-iii/issues/1063), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1064](https://github.com/firefly-iii/firefly-iii/issues/1064), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1066](https://github.com/firefly-iii/firefly-iii/issues/1066), reported by [wtercato](https://github.com/wtercato)
# 4.6.1.1
- Import routine can scan for matching bills, [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956) - Import routine can scan for matching bills, [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956)
- Import will no longer scan for rules, this has become optional. Originally suggested in [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956) by [gavu](https://github.com/gavu) - Import will no longer scan for rules, this has become optional. Originally suggested in [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956) by [gavu](https://github.com/gavu)
- [Issue 1033](https://github.com/firefly-iii/firefly-iii/issues/1033), as reported by [Jumanjii](https://github.com/Jumanjii) - [Issue 1033](https://github.com/firefly-iii/firefly-iii/issues/1033), as reported by [Jumanjii](https://github.com/Jumanjii)

View File

@ -208,14 +208,6 @@ opt/app/.env.heroku
opt/app/.env.sandstorm opt/app/.env.sandstorm
opt/app/.gitattributes opt/app/.gitattributes
opt/app/.sandstorm/.gitattributes opt/app/.sandstorm/.gitattributes
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/action_provision
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/action_set_name
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/creator_uid
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/id
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/index_uuid
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/private_key
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/synced_folders
opt/app/.sandstorm/.vagrant/machines/default/virtualbox/vagrant_cwd
opt/app/.sandstorm/Vagrantfile opt/app/.sandstorm/Vagrantfile
opt/app/.sandstorm/app-graphics/firefly-iii-128.png opt/app/.sandstorm/app-graphics/firefly-iii-128.png
opt/app/.sandstorm/app-graphics/firefly-iii-150.png opt/app/.sandstorm/app-graphics/firefly-iii-150.png
@ -260,6 +252,7 @@ opt/app/app/Events/AdminRequestedTestMessage.php
opt/app/app/Events/Event.php opt/app/app/Events/Event.php
opt/app/app/Events/RegisteredUser.php opt/app/app/Events/RegisteredUser.php
opt/app/app/Events/RequestedNewPassword.php opt/app/app/Events/RequestedNewPassword.php
opt/app/app/Events/RequestedVersionCheckStatus.php
opt/app/app/Events/StoredTransactionJournal.php opt/app/app/Events/StoredTransactionJournal.php
opt/app/app/Events/UpdatedTransactionJournal.php opt/app/app/Events/UpdatedTransactionJournal.php
opt/app/app/Events/UserChangedEmail.php opt/app/app/Events/UserChangedEmail.php
@ -279,6 +272,9 @@ opt/app/app/Export/Exporter/ExporterInterface.php
opt/app/app/Export/ProcessorInterface.php opt/app/app/Export/ProcessorInterface.php
opt/app/app/Generator/Chart/Basic/ChartJsGenerator.php opt/app/app/Generator/Chart/Basic/ChartJsGenerator.php
opt/app/app/Generator/Chart/Basic/GeneratorInterface.php opt/app/app/Generator/Chart/Basic/GeneratorInterface.php
opt/app/app/Generator/Report/Account/MonthReportGenerator.php
opt/app/app/Generator/Report/Account/MultiYearReportGenerator.php
opt/app/app/Generator/Report/Account/YearReportGenerator.php
opt/app/app/Generator/Report/Audit/MonthReportGenerator.php opt/app/app/Generator/Report/Audit/MonthReportGenerator.php
opt/app/app/Generator/Report/Audit/MultiYearReportGenerator.php opt/app/app/Generator/Report/Audit/MultiYearReportGenerator.php
opt/app/app/Generator/Report/Audit/YearReportGenerator.php opt/app/app/Generator/Report/Audit/YearReportGenerator.php
@ -301,6 +297,7 @@ opt/app/app/Handlers/Events/AdminEventHandler.php
opt/app/app/Handlers/Events/StoredJournalEventHandler.php opt/app/app/Handlers/Events/StoredJournalEventHandler.php
opt/app/app/Handlers/Events/UpdatedJournalEventHandler.php opt/app/app/Handlers/Events/UpdatedJournalEventHandler.php
opt/app/app/Handlers/Events/UserEventHandler.php opt/app/app/Handlers/Events/UserEventHandler.php
opt/app/app/Handlers/Events/VersionCheckEventHandler.php
opt/app/app/Helpers/Attachments/AttachmentHelper.php opt/app/app/Helpers/Attachments/AttachmentHelper.php
opt/app/app/Helpers/Attachments/AttachmentHelperInterface.php opt/app/app/Helpers/Attachments/AttachmentHelperInterface.php
opt/app/app/Helpers/Chart/MetaPieChart.php opt/app/app/Helpers/Chart/MetaPieChart.php
@ -339,6 +336,7 @@ opt/app/app/Http/Controllers/AccountController.php
opt/app/app/Http/Controllers/Admin/ConfigurationController.php opt/app/app/Http/Controllers/Admin/ConfigurationController.php
opt/app/app/Http/Controllers/Admin/HomeController.php opt/app/app/Http/Controllers/Admin/HomeController.php
opt/app/app/Http/Controllers/Admin/LinkController.php opt/app/app/Http/Controllers/Admin/LinkController.php
opt/app/app/Http/Controllers/Admin/UpdateController.php
opt/app/app/Http/Controllers/Admin/UserController.php opt/app/app/Http/Controllers/Admin/UserController.php
opt/app/app/Http/Controllers/AttachmentController.php opt/app/app/Http/Controllers/AttachmentController.php
opt/app/app/Http/Controllers/Auth/ForgotPasswordController.php opt/app/app/Http/Controllers/Auth/ForgotPasswordController.php
@ -355,6 +353,7 @@ opt/app/app/Http/Controllers/Chart/BudgetController.php
opt/app/app/Http/Controllers/Chart/BudgetReportController.php opt/app/app/Http/Controllers/Chart/BudgetReportController.php
opt/app/app/Http/Controllers/Chart/CategoryController.php opt/app/app/Http/Controllers/Chart/CategoryController.php
opt/app/app/Http/Controllers/Chart/CategoryReportController.php opt/app/app/Http/Controllers/Chart/CategoryReportController.php
opt/app/app/Http/Controllers/Chart/ExpenseReportController.php
opt/app/app/Http/Controllers/Chart/PiggyBankController.php opt/app/app/Http/Controllers/Chart/PiggyBankController.php
opt/app/app/Http/Controllers/Chart/ReportController.php opt/app/app/Http/Controllers/Chart/ReportController.php
opt/app/app/Http/Controllers/Chart/TagReportController.php opt/app/app/Http/Controllers/Chart/TagReportController.php
@ -363,9 +362,10 @@ opt/app/app/Http/Controllers/CurrencyController.php
opt/app/app/Http/Controllers/ExportController.php opt/app/app/Http/Controllers/ExportController.php
opt/app/app/Http/Controllers/HelpController.php opt/app/app/Http/Controllers/HelpController.php
opt/app/app/Http/Controllers/HomeController.php opt/app/app/Http/Controllers/HomeController.php
opt/app/app/Http/Controllers/Import/BankController.php opt/app/app/Http/Controllers/Import/ConfigurationController.php
opt/app/app/Http/Controllers/Import/FileController.php opt/app/app/Http/Controllers/Import/IndexController.php
opt/app/app/Http/Controllers/ImportController.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/JavascriptController.php
opt/app/app/Http/Controllers/Json/AutoCompleteController.php opt/app/app/Http/Controllers/Json/AutoCompleteController.php
opt/app/app/Http/Controllers/Json/BoxController.php opt/app/app/Http/Controllers/Json/BoxController.php
@ -382,6 +382,7 @@ opt/app/app/Http/Controllers/Report/AccountController.php
opt/app/app/Http/Controllers/Report/BalanceController.php opt/app/app/Http/Controllers/Report/BalanceController.php
opt/app/app/Http/Controllers/Report/BudgetController.php opt/app/app/Http/Controllers/Report/BudgetController.php
opt/app/app/Http/Controllers/Report/CategoryController.php opt/app/app/Http/Controllers/Report/CategoryController.php
opt/app/app/Http/Controllers/Report/ExpenseController.php
opt/app/app/Http/Controllers/Report/OperationsController.php opt/app/app/Http/Controllers/Report/OperationsController.php
opt/app/app/Http/Controllers/ReportController.php opt/app/app/Http/Controllers/ReportController.php
opt/app/app/Http/Controllers/RuleController.php opt/app/app/Http/Controllers/RuleController.php
@ -400,7 +401,8 @@ opt/app/app/Http/Middleware/AuthenticateTwoFactor.php
opt/app/app/Http/Middleware/Binder.php opt/app/app/Http/Middleware/Binder.php
opt/app/app/Http/Middleware/EncryptCookies.php opt/app/app/Http/Middleware/EncryptCookies.php
opt/app/app/Http/Middleware/IsAdmin.php opt/app/app/Http/Middleware/IsAdmin.php
opt/app/app/Http/Middleware/IsLimitedUser.php opt/app/app/Http/Middleware/IsDemoUser.php
opt/app/app/Http/Middleware/IsSandStormUser.php
opt/app/app/Http/Middleware/Range.php opt/app/app/Http/Middleware/Range.php
opt/app/app/Http/Middleware/RedirectIfAuthenticated.php opt/app/app/Http/Middleware/RedirectIfAuthenticated.php
opt/app/app/Http/Middleware/RedirectIfTwoFactorAuthenticated.php opt/app/app/Http/Middleware/RedirectIfTwoFactorAuthenticated.php
@ -420,7 +422,6 @@ opt/app/app/Http/Requests/CurrencyFormRequest.php
opt/app/app/Http/Requests/DeleteAccountFormRequest.php opt/app/app/Http/Requests/DeleteAccountFormRequest.php
opt/app/app/Http/Requests/EmailFormRequest.php opt/app/app/Http/Requests/EmailFormRequest.php
opt/app/app/Http/Requests/ExportFormRequest.php opt/app/app/Http/Requests/ExportFormRequest.php
opt/app/app/Http/Requests/ImportUploadRequest.php
opt/app/app/Http/Requests/JournalFormRequest.php opt/app/app/Http/Requests/JournalFormRequest.php
opt/app/app/Http/Requests/JournalLinkRequest.php opt/app/app/Http/Requests/JournalLinkRequest.php
opt/app/app/Http/Requests/LinkTypeFormRequest.php opt/app/app/Http/Requests/LinkTypeFormRequest.php
@ -441,15 +442,15 @@ opt/app/app/Http/Requests/TestRuleFormRequest.php
opt/app/app/Http/Requests/TokenFormRequest.php opt/app/app/Http/Requests/TokenFormRequest.php
opt/app/app/Http/Requests/UserFormRequest.php opt/app/app/Http/Requests/UserFormRequest.php
opt/app/app/Http/Requests/UserRegistrationRequest.php opt/app/app/Http/Requests/UserRegistrationRequest.php
opt/app/app/Http/breadcrumbs.php opt/app/app/Import/Configuration/ConfiguratorInterface.php
opt/app/app/Import/Configurator/ConfiguratorInterface.php opt/app/app/Import/Configuration/FileConfigurator.php
opt/app/app/Import/Configurator/CsvConfigurator.php opt/app/app/Import/Configuration/SpectreConfigurator.php
opt/app/app/Import/Converter/Amount.php opt/app/app/Import/Converter/Amount.php
opt/app/app/Import/Converter/AmountCredit.php opt/app/app/Import/Converter/AmountCredit.php
opt/app/app/Import/Converter/AmountDebet.php opt/app/app/Import/Converter/AmountDebit.php
opt/app/app/Import/Converter/ConverterInterface.php opt/app/app/Import/Converter/ConverterInterface.php
opt/app/app/Import/Converter/INGDebetCredit.php opt/app/app/Import/Converter/INGDebitCredit.php
opt/app/app/Import/Converter/RabobankDebetCredit.php opt/app/app/Import/Converter/RabobankDebitCredit.php
opt/app/app/Import/FileProcessor/CsvProcessor.php opt/app/app/Import/FileProcessor/CsvProcessor.php
opt/app/app/Import/FileProcessor/FileProcessorInterface.php opt/app/app/Import/FileProcessor/FileProcessorInterface.php
opt/app/app/Import/Logging/CommandHandler.php opt/app/app/Import/Logging/CommandHandler.php
@ -472,7 +473,13 @@ opt/app/app/Import/Object/ImportBudget.php
opt/app/app/Import/Object/ImportCategory.php opt/app/app/Import/Object/ImportCategory.php
opt/app/app/Import/Object/ImportCurrency.php opt/app/app/Import/Object/ImportCurrency.php
opt/app/app/Import/Object/ImportJournal.php opt/app/app/Import/Object/ImportJournal.php
opt/app/app/Import/Routine/ImportRoutine.php opt/app/app/Import/Prerequisites/BunqPrerequisites.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/FileRoutine.php
opt/app/app/Import/Routine/RoutineInterface.php
opt/app/app/Import/Routine/SpectreRoutine.php
opt/app/app/Import/Specifics/AbnAmroDescription.php opt/app/app/Import/Specifics/AbnAmroDescription.php
opt/app/app/Import/Specifics/IngDescription.php opt/app/app/Import/Specifics/IngDescription.php
opt/app/app/Import/Specifics/PresidentsChoice.php opt/app/app/Import/Specifics/PresidentsChoice.php
@ -612,8 +619,18 @@ opt/app/app/Services/Bunq/Token/InstallationToken.php
opt/app/app/Services/Bunq/Token/SessionToken.php opt/app/app/Services/Bunq/Token/SessionToken.php
opt/app/app/Services/Currency/ExchangeRateInterface.php opt/app/app/Services/Currency/ExchangeRateInterface.php
opt/app/app/Services/Currency/FixerIO.php opt/app/app/Services/Currency/FixerIO.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/Password/PwndVerifier.php opt/app/app/Services/Password/PwndVerifier.php
opt/app/app/Services/Password/Verifier.php opt/app/app/Services/Password/Verifier.php
opt/app/app/Services/Spectre/Object/Customer.php
opt/app/app/Services/Spectre/Object/SpectreObject.php
opt/app/app/Services/Spectre/Object/Token.php
opt/app/app/Services/Spectre/Request/CreateTokenRequest.php
opt/app/app/Services/Spectre/Request/NewCustomerRequest.php
opt/app/app/Services/Spectre/Request/SpectreRequest.php
opt/app/app/Support/Amount.php opt/app/app/Support/Amount.php
opt/app/app/Support/Binder/AccountList.php opt/app/app/Support/Binder/AccountList.php
opt/app/app/Support/Binder/BinderInterface.php opt/app/app/Support/Binder/BinderInterface.php
@ -637,13 +654,12 @@ opt/app/app/Support/Facades/Preferences.php
opt/app/app/Support/Facades/Steam.php opt/app/app/Support/Facades/Steam.php
opt/app/app/Support/FireflyConfig.php opt/app/app/Support/FireflyConfig.php
opt/app/app/Support/Import/Configuration/ConfigurationInterface.php opt/app/app/Support/Import/Configuration/ConfigurationInterface.php
opt/app/app/Support/Import/Configuration/Csv/Initial.php opt/app/app/Support/Import/Configuration/File/Initial.php
opt/app/app/Support/Import/Configuration/Csv/Map.php opt/app/app/Support/Import/Configuration/File/Map.php
opt/app/app/Support/Import/Configuration/Csv/Roles.php opt/app/app/Support/Import/Configuration/File/Roles.php
opt/app/app/Support/Import/Configuration/File/Upload.php
opt/app/app/Support/Import/Information/BunqInformation.php opt/app/app/Support/Import/Information/BunqInformation.php
opt/app/app/Support/Import/Information/InformationInterface.php opt/app/app/Support/Import/Information/InformationInterface.php
opt/app/app/Support/Import/Prerequisites/BunqPrerequisites.php
opt/app/app/Support/Import/Prerequisites/PrerequisitesInterface.php
opt/app/app/Support/Models/TransactionJournalTrait.php opt/app/app/Support/Models/TransactionJournalTrait.php
opt/app/app/Support/Navigation.php opt/app/app/Support/Navigation.php
opt/app/app/Support/Preferences.php opt/app/app/Support/Preferences.php
@ -730,12 +746,15 @@ opt/app/composer.lock
opt/app/composer.phar opt/app/composer.phar
opt/app/config/app.php opt/app/config/app.php
opt/app/config/auth.php opt/app/config/auth.php
opt/app/config/breadcrumbs.php
opt/app/config/broadcasting.php opt/app/config/broadcasting.php
opt/app/config/cache.php opt/app/config/cache.php
opt/app/config/csv.php opt/app/config/csv.php
opt/app/config/database.php opt/app/config/database.php
opt/app/config/filesystems.php opt/app/config/filesystems.php
opt/app/config/firefly.php opt/app/config/firefly.php
opt/app/config/google2fa.php
opt/app/config/import.php
opt/app/config/intro.php opt/app/config/intro.php
opt/app/config/mail.php opt/app/config/mail.php
opt/app/config/queue.php opt/app/config/queue.php
@ -766,9 +785,6 @@ opt/app/database/seeds/LinkTypeSeeder.php
opt/app/database/seeds/PermissionSeeder.php opt/app/database/seeds/PermissionSeeder.php
opt/app/database/seeds/TransactionCurrencySeeder.php opt/app/database/seeds/TransactionCurrencySeeder.php
opt/app/database/seeds/TransactionTypeSeeder.php opt/app/database/seeds/TransactionTypeSeeder.php
opt/app/docker-compose.dockerhub.yml
opt/app/docker-compose.override.yml
opt/app/docker-compose.prod.yml
opt/app/docker-compose.yml opt/app/docker-compose.yml
opt/app/nginx_app.conf opt/app/nginx_app.conf
opt/app/phpunit.coverage.xml opt/app/phpunit.coverage.xml
@ -850,6 +866,10 @@ opt/app/public/images/image.png
opt/app/public/images/loading-small.gif opt/app/public/images/loading-small.gif
opt/app/public/images/loading-wide.gif opt/app/public/images/loading-wide.gif
opt/app/public/images/logos/bunq.png opt/app/public/images/logos/bunq.png
opt/app/public/images/logos/csv.png
opt/app/public/images/logos/file.png
opt/app/public/images/logos/plaid.png
opt/app/public/images/logos/spectre.png
opt/app/public/images/page_green.png opt/app/public/images/page_green.png
opt/app/public/images/page_white_acrobat.png opt/app/public/images/page_white_acrobat.png
opt/app/public/index.php opt/app/public/index.php
@ -858,6 +878,7 @@ opt/app/public/js/ff/accounts/edit-reconciliation.js
opt/app/public/js/ff/accounts/edit.js opt/app/public/js/ff/accounts/edit.js
opt/app/public/js/ff/accounts/reconcile.js opt/app/public/js/ff/accounts/reconcile.js
opt/app/public/js/ff/accounts/show.js opt/app/public/js/ff/accounts/show.js
opt/app/public/js/ff/admin/update/index.js
opt/app/public/js/ff/bills/create.js opt/app/public/js/ff/bills/create.js
opt/app/public/js/ff/bills/edit.js opt/app/public/js/ff/bills/edit.js
opt/app/public/js/ff/bills/show.js opt/app/public/js/ff/bills/show.js
@ -880,17 +901,16 @@ opt/app/public/js/ff/piggy-banks/edit.js
opt/app/public/js/ff/piggy-banks/index.js opt/app/public/js/ff/piggy-banks/index.js
opt/app/public/js/ff/piggy-banks/show.js opt/app/public/js/ff/piggy-banks/show.js
opt/app/public/js/ff/preferences/index.js opt/app/public/js/ff/preferences/index.js
opt/app/public/js/ff/reports/account/month.js
opt/app/public/js/ff/reports/all.js
opt/app/public/js/ff/reports/audit/all.js opt/app/public/js/ff/reports/audit/all.js
opt/app/public/js/ff/reports/budget/all.js
opt/app/public/js/ff/reports/budget/month.js opt/app/public/js/ff/reports/budget/month.js
opt/app/public/js/ff/reports/category/all.js
opt/app/public/js/ff/reports/category/month.js opt/app/public/js/ff/reports/category/month.js
opt/app/public/js/ff/reports/default/all.js opt/app/public/js/ff/reports/default/all.js
opt/app/public/js/ff/reports/default/month.js opt/app/public/js/ff/reports/default/month.js
opt/app/public/js/ff/reports/default/multi-year.js opt/app/public/js/ff/reports/default/multi-year.js
opt/app/public/js/ff/reports/default/year.js opt/app/public/js/ff/reports/default/year.js
opt/app/public/js/ff/reports/index.js opt/app/public/js/ff/reports/index.js
opt/app/public/js/ff/reports/tag/all.js
opt/app/public/js/ff/reports/tag/month.js opt/app/public/js/ff/reports/tag/month.js
opt/app/public/js/ff/rules/create-edit.js opt/app/public/js/ff/rules/create-edit.js
opt/app/public/js/ff/rules/index.js opt/app/public/js/ff/rules/index.js
@ -975,6 +995,7 @@ opt/app/resources/lang/de_DE/demo.php
opt/app/resources/lang/de_DE/firefly.php opt/app/resources/lang/de_DE/firefly.php
opt/app/resources/lang/de_DE/form.php opt/app/resources/lang/de_DE/form.php
opt/app/resources/lang/de_DE/help.php opt/app/resources/lang/de_DE/help.php
opt/app/resources/lang/de_DE/import.php
opt/app/resources/lang/de_DE/intro.php opt/app/resources/lang/de_DE/intro.php
opt/app/resources/lang/de_DE/list.php opt/app/resources/lang/de_DE/list.php
opt/app/resources/lang/de_DE/pagination.php opt/app/resources/lang/de_DE/pagination.php
@ -988,6 +1009,7 @@ opt/app/resources/lang/en_US/csv.php
opt/app/resources/lang/en_US/demo.php opt/app/resources/lang/en_US/demo.php
opt/app/resources/lang/en_US/firefly.php opt/app/resources/lang/en_US/firefly.php
opt/app/resources/lang/en_US/form.php opt/app/resources/lang/en_US/form.php
opt/app/resources/lang/en_US/import.php
opt/app/resources/lang/en_US/intro.php opt/app/resources/lang/en_US/intro.php
opt/app/resources/lang/en_US/list.php opt/app/resources/lang/en_US/list.php
opt/app/resources/lang/en_US/pagination.php opt/app/resources/lang/en_US/pagination.php
@ -1002,11 +1024,26 @@ opt/app/resources/lang/fr_FR/demo.php
opt/app/resources/lang/fr_FR/firefly.php opt/app/resources/lang/fr_FR/firefly.php
opt/app/resources/lang/fr_FR/form.php opt/app/resources/lang/fr_FR/form.php
opt/app/resources/lang/fr_FR/help.php opt/app/resources/lang/fr_FR/help.php
opt/app/resources/lang/fr_FR/import.php
opt/app/resources/lang/fr_FR/intro.php opt/app/resources/lang/fr_FR/intro.php
opt/app/resources/lang/fr_FR/list.php opt/app/resources/lang/fr_FR/list.php
opt/app/resources/lang/fr_FR/pagination.php opt/app/resources/lang/fr_FR/pagination.php
opt/app/resources/lang/fr_FR/passwords.php opt/app/resources/lang/fr_FR/passwords.php
opt/app/resources/lang/fr_FR/validation.php opt/app/resources/lang/fr_FR/validation.php
opt/app/resources/lang/id_ID/auth.php
opt/app/resources/lang/id_ID/bank.php
opt/app/resources/lang/id_ID/breadcrumbs.php
opt/app/resources/lang/id_ID/config.php
opt/app/resources/lang/id_ID/csv.php
opt/app/resources/lang/id_ID/demo.php
opt/app/resources/lang/id_ID/firefly.php
opt/app/resources/lang/id_ID/form.php
opt/app/resources/lang/id_ID/import.php
opt/app/resources/lang/id_ID/intro.php
opt/app/resources/lang/id_ID/list.php
opt/app/resources/lang/id_ID/pagination.php
opt/app/resources/lang/id_ID/passwords.php
opt/app/resources/lang/id_ID/validation.php
opt/app/resources/lang/nl_NL/auth.php opt/app/resources/lang/nl_NL/auth.php
opt/app/resources/lang/nl_NL/bank.php opt/app/resources/lang/nl_NL/bank.php
opt/app/resources/lang/nl_NL/breadcrumbs.php opt/app/resources/lang/nl_NL/breadcrumbs.php
@ -1016,6 +1053,7 @@ opt/app/resources/lang/nl_NL/demo.php
opt/app/resources/lang/nl_NL/firefly.php opt/app/resources/lang/nl_NL/firefly.php
opt/app/resources/lang/nl_NL/form.php opt/app/resources/lang/nl_NL/form.php
opt/app/resources/lang/nl_NL/help.php opt/app/resources/lang/nl_NL/help.php
opt/app/resources/lang/nl_NL/import.php
opt/app/resources/lang/nl_NL/intro.php opt/app/resources/lang/nl_NL/intro.php
opt/app/resources/lang/nl_NL/list.php opt/app/resources/lang/nl_NL/list.php
opt/app/resources/lang/nl_NL/pagination.php opt/app/resources/lang/nl_NL/pagination.php
@ -1030,6 +1068,7 @@ opt/app/resources/lang/pl_PL/demo.php
opt/app/resources/lang/pl_PL/firefly.php opt/app/resources/lang/pl_PL/firefly.php
opt/app/resources/lang/pl_PL/form.php opt/app/resources/lang/pl_PL/form.php
opt/app/resources/lang/pl_PL/help.php opt/app/resources/lang/pl_PL/help.php
opt/app/resources/lang/pl_PL/import.php
opt/app/resources/lang/pl_PL/intro.php opt/app/resources/lang/pl_PL/intro.php
opt/app/resources/lang/pl_PL/list.php opt/app/resources/lang/pl_PL/list.php
opt/app/resources/lang/pl_PL/pagination.php opt/app/resources/lang/pl_PL/pagination.php
@ -1057,6 +1096,7 @@ opt/app/resources/views/admin/link/delete.twig
opt/app/resources/views/admin/link/edit.twig opt/app/resources/views/admin/link/edit.twig
opt/app/resources/views/admin/link/index.twig opt/app/resources/views/admin/link/index.twig
opt/app/resources/views/admin/link/show.twig opt/app/resources/views/admin/link/show.twig
opt/app/resources/views/admin/update/index.twig
opt/app/resources/views/admin/users/delete.twig opt/app/resources/views/admin/users/delete.twig
opt/app/resources/views/admin/users/edit.twig opt/app/resources/views/admin/users/edit.twig
opt/app/resources/views/admin/users/index.twig opt/app/resources/views/admin/users/index.twig
@ -1151,13 +1191,17 @@ opt/app/resources/views/form/text.twig
opt/app/resources/views/form/textarea.twig opt/app/resources/views/form/textarea.twig
opt/app/resources/views/import/bank/form.twig opt/app/resources/views/import/bank/form.twig
opt/app/resources/views/import/bunq/prerequisites.twig opt/app/resources/views/import/bunq/prerequisites.twig
opt/app/resources/views/import/csv/initial.twig opt/app/resources/views/import/file/initial.twig
opt/app/resources/views/import/csv/map.twig opt/app/resources/views/import/file/map.twig
opt/app/resources/views/import/csv/roles.twig opt/app/resources/views/import/file/roles.twig
opt/app/resources/views/import/file/finished.twig opt/app/resources/views/import/file/upload.twig
opt/app/resources/views/import/file/index.twig
opt/app/resources/views/import/file/status.twig
opt/app/resources/views/import/index.twig opt/app/resources/views/import/index.twig
opt/app/resources/views/import/spectre/input-fields.twig
opt/app/resources/views/import/spectre/prerequisites.twig
opt/app/resources/views/import/spectre/redirect.twig
opt/app/resources/views/import/spectre/select-country.twig
opt/app/resources/views/import/spectre/select-provider.twig
opt/app/resources/views/import/status.twig
opt/app/resources/views/index.twig opt/app/resources/views/index.twig
opt/app/resources/views/javascript/accounts.twig opt/app/resources/views/javascript/accounts.twig
opt/app/resources/views/javascript/currencies.twig opt/app/resources/views/javascript/currencies.twig
@ -1204,6 +1248,7 @@ opt/app/resources/views/profile/change-email.twig
opt/app/resources/views/profile/change-password.twig opt/app/resources/views/profile/change-password.twig
opt/app/resources/views/profile/delete-account.twig opt/app/resources/views/profile/delete-account.twig
opt/app/resources/views/profile/index.twig opt/app/resources/views/profile/index.twig
opt/app/resources/views/reports/account/report.twig
opt/app/resources/views/reports/audit/report.twig opt/app/resources/views/reports/audit/report.twig
opt/app/resources/views/reports/budget/month.twig opt/app/resources/views/reports/budget/month.twig
opt/app/resources/views/reports/category/month.twig opt/app/resources/views/reports/category/month.twig
@ -1211,6 +1256,7 @@ opt/app/resources/views/reports/default/month.twig
opt/app/resources/views/reports/default/multi-year.twig opt/app/resources/views/reports/default/multi-year.twig
opt/app/resources/views/reports/default/year.twig opt/app/resources/views/reports/default/year.twig
opt/app/resources/views/reports/index.twig opt/app/resources/views/reports/index.twig
opt/app/resources/views/reports/options/account.twig
opt/app/resources/views/reports/options/budget.twig opt/app/resources/views/reports/options/budget.twig
opt/app/resources/views/reports/options/category.twig opt/app/resources/views/reports/options/category.twig
opt/app/resources/views/reports/options/no-options.twig opt/app/resources/views/reports/options/no-options.twig
@ -1222,10 +1268,14 @@ opt/app/resources/views/reports/partials/budget-period.twig
opt/app/resources/views/reports/partials/budgets.twig opt/app/resources/views/reports/partials/budgets.twig
opt/app/resources/views/reports/partials/categories.twig opt/app/resources/views/reports/partials/categories.twig
opt/app/resources/views/reports/partials/category-period.twig opt/app/resources/views/reports/partials/category-period.twig
opt/app/resources/views/reports/partials/exp-budgets.twig
opt/app/resources/views/reports/partials/exp-categories.twig
opt/app/resources/views/reports/partials/exp-not-grouped.twig
opt/app/resources/views/reports/partials/income-expenses.twig opt/app/resources/views/reports/partials/income-expenses.twig
opt/app/resources/views/reports/partials/journals-audit.twig opt/app/resources/views/reports/partials/journals-audit.twig
opt/app/resources/views/reports/partials/operations.twig opt/app/resources/views/reports/partials/operations.twig
opt/app/resources/views/reports/partials/tags.twig opt/app/resources/views/reports/partials/tags.twig
opt/app/resources/views/reports/partials/top-transactions.twig
opt/app/resources/views/reports/tag/month.twig opt/app/resources/views/reports/tag/month.twig
opt/app/resources/views/rules/index.twig opt/app/resources/views/rules/index.twig
opt/app/resources/views/rules/partials/action.twig opt/app/resources/views/rules/partials/action.twig
@ -1246,6 +1296,7 @@ opt/app/resources/views/tags/delete.twig
opt/app/resources/views/tags/edit.twig opt/app/resources/views/tags/edit.twig
opt/app/resources/views/tags/index.twig opt/app/resources/views/tags/index.twig
opt/app/resources/views/tags/show.twig opt/app/resources/views/tags/show.twig
opt/app/resources/views/test/test.twig
opt/app/resources/views/transactions/convert.twig opt/app/resources/views/transactions/convert.twig
opt/app/resources/views/transactions/index.twig opt/app/resources/views/transactions/index.twig
opt/app/resources/views/transactions/links/delete.twig opt/app/resources/views/transactions/links/delete.twig
@ -1257,6 +1308,7 @@ opt/app/resources/views/transactions/single/delete.twig
opt/app/resources/views/transactions/single/edit.twig opt/app/resources/views/transactions/single/edit.twig
opt/app/resources/views/transactions/split/edit.twig opt/app/resources/views/transactions/split/edit.twig
opt/app/routes/api.php opt/app/routes/api.php
opt/app/routes/breadcrumbs.php
opt/app/routes/channels.php opt/app/routes/channels.php
opt/app/routes/console.php opt/app/routes/console.php
opt/app/routes/web.php opt/app/routes/web.php
@ -1325,14 +1377,6 @@ 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/bacon/bacon-qr-code/tests/bootstrap.php
opt/app/vendor/bin/commonmark opt/app/vendor/bin/commonmark
opt/app/vendor/bin/doctrine-dbal opt/app/vendor/bin/doctrine-dbal
opt/app/vendor/christian-riesen/base32/LICENSE
opt/app/vendor/christian-riesen/base32/README.md
opt/app/vendor/christian-riesen/base32/build.xml
opt/app/vendor/christian-riesen/base32/composer.json
opt/app/vendor/christian-riesen/base32/phpunit.xml.dist
opt/app/vendor/christian-riesen/base32/src/Base32.php
opt/app/vendor/christian-riesen/base32/tests/Base32Test.php
opt/app/vendor/christian-riesen/base32/tests/bootstrap.php
opt/app/vendor/composer/ClassLoader.php opt/app/vendor/composer/ClassLoader.php
opt/app/vendor/composer/LICENSE opt/app/vendor/composer/LICENSE
opt/app/vendor/composer/autoload_classmap.php opt/app/vendor/composer/autoload_classmap.php
@ -1342,37 +1386,24 @@ opt/app/vendor/composer/autoload_psr4.php
opt/app/vendor/composer/autoload_real.php opt/app/vendor/composer/autoload_real.php
opt/app/vendor/composer/autoload_static.php opt/app/vendor/composer/autoload_static.php
opt/app/vendor/composer/installed.json opt/app/vendor/composer/installed.json
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/.editorconfig
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/README.md
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/RELEASE-CHECKLIST.md
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/composer.json opt/app/vendor/davejamesmiller/laravel-breadcrumbs/composer.json
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/config/breadcrumbs.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/config/breadcrumbs.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/scripts/test-coverage.sh opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/BreadcrumbsException.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/CurrentRoute.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/BreadcrumbsGenerator.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Exception.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/BreadcrumbsManager.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Facade.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/BreadcrumbsServiceProvider.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Generator.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Exceptions/DuplicateBreadcrumbException.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Manager.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Exceptions/InvalidBreadcrumbException.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/ServiceProvider.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Exceptions/UnnamedRouteException.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/View.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Exceptions/ViewNotSetException.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/TestCase.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Facades/Breadcrumbs.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/bootstrap.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/fixtures/CustomServiceProvider.html
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/fixtures/DependantServiceProvider.html
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/fixtures/bootstrap2.html
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/fixtures/bootstrap3.html
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/fixtures/integration.html
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/integration/CustomServiceProviderTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/integration/DependantServiceProviderErrorTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/integration/DependantServiceProviderTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/integration/IntegrationTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/unit/CurrentRouteTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/unit/FacadeTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/unit/GeneratorTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/unit/ManagerTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/tests/unit/ViewTest.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap2.blade.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap2.blade.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap3.blade.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap3.blade.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap4.blade.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bulma.blade.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/foundation6.blade.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/json-ld.php
opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/materialize.blade.php
opt/app/vendor/doctrine/annotations/CHANGELOG.md opt/app/vendor/doctrine/annotations/CHANGELOG.md
opt/app/vendor/doctrine/annotations/LICENSE opt/app/vendor/doctrine/annotations/LICENSE
opt/app/vendor/doctrine/annotations/README.md opt/app/vendor/doctrine/annotations/README.md
@ -2188,6 +2219,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Events/CallQueuedListener.php
opt/app/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php opt/app/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
opt/app/vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php
opt/app/vendor/laravel/framework/src/Illuminate/Events/composer.json opt/app/vendor/laravel/framework/src/Illuminate/Events/composer.json
opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/Cache.php
opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php
opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemAdapter.php opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemAdapter.php
opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemManager.php opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemManager.php
@ -2663,6 +2695,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/MessageBag.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/NamespacedItemResolver.php opt/app/vendor/laravel/framework/src/Illuminate/Support/NamespacedItemResolver.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/Optional.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Optional.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/ProcessUtils.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/Str.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Str.php
opt/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/BusFake.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/BusFake.php
@ -2865,23 +2898,29 @@ opt/app/vendor/league/commonmark/src/Util/Html5Entities.php
opt/app/vendor/league/commonmark/src/Util/LinkParserHelper.php opt/app/vendor/league/commonmark/src/Util/LinkParserHelper.php
opt/app/vendor/league/commonmark/src/Util/RegexHelper.php opt/app/vendor/league/commonmark/src/Util/RegexHelper.php
opt/app/vendor/league/commonmark/src/Util/UrlEncoder.php opt/app/vendor/league/commonmark/src/Util/UrlEncoder.php
opt/app/vendor/league/commonmark/src/Util/Xml.php
opt/app/vendor/league/csv/LICENSE opt/app/vendor/league/csv/LICENSE
opt/app/vendor/league/csv/autoload.php opt/app/vendor/league/csv/autoload.php
opt/app/vendor/league/csv/composer.json opt/app/vendor/league/csv/composer.json
opt/app/vendor/league/csv/src/AbstractCsv.php opt/app/vendor/league/csv/src/AbstractCsv.php
opt/app/vendor/league/csv/src/Config/Controls.php opt/app/vendor/league/csv/src/ByteSequence.php
opt/app/vendor/league/csv/src/Config/Output.php opt/app/vendor/league/csv/src/CannotInsertRecord.php
opt/app/vendor/league/csv/src/Exception/InvalidRowException.php opt/app/vendor/league/csv/src/CharsetConverter.php
opt/app/vendor/league/csv/src/Modifier/MapIterator.php opt/app/vendor/league/csv/src/ColumnConsistency.php
opt/app/vendor/league/csv/src/Modifier/QueryFilter.php opt/app/vendor/league/csv/src/EncloseField.php
opt/app/vendor/league/csv/src/Modifier/RowFilter.php opt/app/vendor/league/csv/src/EscapeFormula.php
opt/app/vendor/league/csv/src/Modifier/StreamFilter.php opt/app/vendor/league/csv/src/Exception.php
opt/app/vendor/league/csv/src/Modifier/StreamIterator.php opt/app/vendor/league/csv/src/HTMLConverter.php
opt/app/vendor/league/csv/src/Plugin/ColumnConsistencyValidator.php opt/app/vendor/league/csv/src/MapIterator.php
opt/app/vendor/league/csv/src/Plugin/ForbiddenNullValuesValidator.php opt/app/vendor/league/csv/src/RFC4180Field.php
opt/app/vendor/league/csv/src/Plugin/SkipNullValuesFormatter.php
opt/app/vendor/league/csv/src/Reader.php opt/app/vendor/league/csv/src/Reader.php
opt/app/vendor/league/csv/src/ResultSet.php
opt/app/vendor/league/csv/src/Statement.php
opt/app/vendor/league/csv/src/Stream.php
opt/app/vendor/league/csv/src/Writer.php opt/app/vendor/league/csv/src/Writer.php
opt/app/vendor/league/csv/src/XMLConverter.php
opt/app/vendor/league/csv/src/functions.php
opt/app/vendor/league/csv/src/functions_include.php
opt/app/vendor/league/flysystem/LICENSE opt/app/vendor/league/flysystem/LICENSE
opt/app/vendor/league/flysystem/composer.json opt/app/vendor/league/flysystem/composer.json
opt/app/vendor/league/flysystem/docs/CNAME opt/app/vendor/league/flysystem/docs/CNAME
@ -3229,6 +3268,31 @@ opt/app/vendor/nesbot/carbon/src/Carbon/Lang/uz.php
opt/app/vendor/nesbot/carbon/src/Carbon/Lang/vi.php opt/app/vendor/nesbot/carbon/src/Carbon/Lang/vi.php
opt/app/vendor/nesbot/carbon/src/Carbon/Lang/zh.php opt/app/vendor/nesbot/carbon/src/Carbon/Lang/zh.php
opt/app/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php opt/app/vendor/nesbot/carbon/src/Carbon/Lang/zh_TW.php
opt/app/vendor/paragonie/constant_time_encoding/LICENSE.txt
opt/app/vendor/paragonie/constant_time_encoding/README.md
opt/app/vendor/paragonie/constant_time_encoding/composer.json
opt/app/vendor/paragonie/constant_time_encoding/phpunit.xml.dist
opt/app/vendor/paragonie/constant_time_encoding/psalm.xml
opt/app/vendor/paragonie/constant_time_encoding/src/Base32.php
opt/app/vendor/paragonie/constant_time_encoding/src/Base32Hex.php
opt/app/vendor/paragonie/constant_time_encoding/src/Base64.php
opt/app/vendor/paragonie/constant_time_encoding/src/Base64DotSlash.php
opt/app/vendor/paragonie/constant_time_encoding/src/Base64DotSlashOrdered.php
opt/app/vendor/paragonie/constant_time_encoding/src/Base64UrlSafe.php
opt/app/vendor/paragonie/constant_time_encoding/src/Binary.php
opt/app/vendor/paragonie/constant_time_encoding/src/EncoderInterface.php
opt/app/vendor/paragonie/constant_time_encoding/src/Encoding.php
opt/app/vendor/paragonie/constant_time_encoding/src/Hex.php
opt/app/vendor/paragonie/constant_time_encoding/src/RFC4648.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base32HexTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base32Test.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base64DotSlashOrderedTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base64DotSlashTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base64Test.php
opt/app/vendor/paragonie/constant_time_encoding/tests/Base64UrlSafeTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/EncodingTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/HexTest.php
opt/app/vendor/paragonie/constant_time_encoding/tests/RFC4648Test.php
opt/app/vendor/paragonie/random_compat/LICENSE opt/app/vendor/paragonie/random_compat/LICENSE
opt/app/vendor/paragonie/random_compat/build-phar.sh opt/app/vendor/paragonie/random_compat/build-phar.sh
opt/app/vendor/paragonie/random_compat/composer.json opt/app/vendor/paragonie/random_compat/composer.json
@ -3247,19 +3311,45 @@ opt/app/vendor/paragonie/random_compat/lib/random_int.php
opt/app/vendor/paragonie/random_compat/other/build_phar.php opt/app/vendor/paragonie/random_compat/other/build_phar.php
opt/app/vendor/paragonie/random_compat/psalm-autoload.php opt/app/vendor/paragonie/random_compat/psalm-autoload.php
opt/app/vendor/paragonie/random_compat/psalm.xml opt/app/vendor/paragonie/random_compat/psalm.xml
opt/app/vendor/pragmarx/google2fa-laravel/LICENSE
opt/app/vendor/pragmarx/google2fa-laravel/changelog.md
opt/app/vendor/pragmarx/google2fa-laravel/composer.json
opt/app/vendor/pragmarx/google2fa-laravel/docs/middleware.jpg
opt/app/vendor/pragmarx/google2fa-laravel/phpspec.yml
opt/app/vendor/pragmarx/google2fa-laravel/readme.md
opt/app/vendor/pragmarx/google2fa-laravel/src/Events/OneTimePasswordRequested.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Exceptions/InvalidOneTimePassword.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Exceptions/InvalidSecretKey.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Facade.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Middleware.php
opt/app/vendor/pragmarx/google2fa-laravel/src/ServiceProvider.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Auth.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Authenticator.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Config.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Constants.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/ErrorBag.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Input.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Request.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Response.php
opt/app/vendor/pragmarx/google2fa-laravel/src/Support/Session.php
opt/app/vendor/pragmarx/google2fa-laravel/src/config/config.php
opt/app/vendor/pragmarx/google2fa-laravel/tests/spec/Support/AuthenticatorSpec.php
opt/app/vendor/pragmarx/google2fa-laravel/upgrading.md
opt/app/vendor/pragmarx/google2fa/LICENSE opt/app/vendor/pragmarx/google2fa/LICENSE
opt/app/vendor/pragmarx/google2fa/changelog.md opt/app/vendor/pragmarx/google2fa/changelog.md
opt/app/vendor/pragmarx/google2fa/composer.json opt/app/vendor/pragmarx/google2fa/composer.json
opt/app/vendor/pragmarx/google2fa/phpspec.yml opt/app/vendor/pragmarx/google2fa/docs/playground.jpg
opt/app/vendor/pragmarx/google2fa/readme.md opt/app/vendor/pragmarx/google2fa/readme.md
opt/app/vendor/pragmarx/google2fa/src/Contracts/Google2FA.php opt/app/vendor/pragmarx/google2fa/src/Exceptions/IncompatibleWithGoogleAuthenticatorException.php
opt/app/vendor/pragmarx/google2fa/src/Exceptions/InvalidCharactersException.php opt/app/vendor/pragmarx/google2fa/src/Exceptions/InvalidCharactersException.php
opt/app/vendor/pragmarx/google2fa/src/Exceptions/SecretKeyTooShortException.php opt/app/vendor/pragmarx/google2fa/src/Exceptions/SecretKeyTooShortException.php
opt/app/vendor/pragmarx/google2fa/src/Google2FA.php opt/app/vendor/pragmarx/google2fa/src/Google2FA.php
opt/app/vendor/pragmarx/google2fa/src/Support/Base32.php
opt/app/vendor/pragmarx/google2fa/src/Support/Constants.php
opt/app/vendor/pragmarx/google2fa/src/Support/QRCode.php
opt/app/vendor/pragmarx/google2fa/src/Support/Url.php opt/app/vendor/pragmarx/google2fa/src/Support/Url.php
opt/app/vendor/pragmarx/google2fa/src/Vendor/Laravel/Facade.php opt/app/vendor/pragmarx/google2fa/tests/Google2FATest.php
opt/app/vendor/pragmarx/google2fa/src/Vendor/Laravel/ServiceProvider.php opt/app/vendor/pragmarx/google2fa/tests/bootstrap.php
opt/app/vendor/pragmarx/google2fa/tests/spec/Google2FASpec.php
opt/app/vendor/pragmarx/google2fa/upgrading.md opt/app/vendor/pragmarx/google2fa/upgrading.md
opt/app/vendor/psr/container/LICENSE opt/app/vendor/psr/container/LICENSE
opt/app/vendor/psr/container/README.md opt/app/vendor/psr/container/README.md
@ -4245,10 +4335,11 @@ opt/app/vendor/symfony/debug/Tests/Fixtures/reallyNotPsr0.php
opt/app/vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php opt/app/vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php
opt/app/vendor/symfony/debug/Tests/HeaderMock.php opt/app/vendor/symfony/debug/Tests/HeaderMock.php
opt/app/vendor/symfony/debug/Tests/MockExceptionHandler.php opt/app/vendor/symfony/debug/Tests/MockExceptionHandler.php
opt/app/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt
opt/app/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt
opt/app/vendor/symfony/debug/composer.json opt/app/vendor/symfony/debug/composer.json
opt/app/vendor/symfony/debug/phpunit.xml.dist opt/app/vendor/symfony/debug/phpunit.xml.dist
opt/app/vendor/symfony/event-dispatcher/CHANGELOG.md opt/app/vendor/symfony/event-dispatcher/CHANGELOG.md
opt/app/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php
opt/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php opt/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
opt/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php opt/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
opt/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php opt/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php
@ -4262,7 +4353,6 @@ opt/app/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php
opt/app/vendor/symfony/event-dispatcher/LICENSE opt/app/vendor/symfony/event-dispatcher/LICENSE
opt/app/vendor/symfony/event-dispatcher/README.md opt/app/vendor/symfony/event-dispatcher/README.md
opt/app/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php opt/app/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php
opt/app/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php
opt/app/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php opt/app/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php
opt/app/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php opt/app/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php
opt/app/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php opt/app/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php
@ -4453,6 +4543,8 @@ opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/st
opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php
opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected
opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php
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/MemcacheSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php
opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php opt/app/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php
@ -4529,9 +4621,12 @@ opt/app/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueRe
opt/app/vendor/symfony/http-kernel/DependencyInjection/Extension.php opt/app/vendor/symfony/http-kernel/DependencyInjection/Extension.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php opt/app/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php opt/app/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php opt/app/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php opt/app/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php opt/app/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php
opt/app/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php
opt/app/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php opt/app/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php
opt/app/vendor/symfony/http-kernel/Event/FilterControllerEvent.php opt/app/vendor/symfony/http-kernel/Event/FilterControllerEvent.php
opt/app/vendor/symfony/http-kernel/Event/FilterResponseEvent.php opt/app/vendor/symfony/http-kernel/Event/FilterResponseEvent.php
@ -4600,11 +4695,14 @@ opt/app/vendor/symfony/http-kernel/KernelEvents.php
opt/app/vendor/symfony/http-kernel/KernelInterface.php opt/app/vendor/symfony/http-kernel/KernelInterface.php
opt/app/vendor/symfony/http-kernel/LICENSE opt/app/vendor/symfony/http-kernel/LICENSE
opt/app/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php opt/app/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php
opt/app/vendor/symfony/http-kernel/Log/Logger.php
opt/app/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php opt/app/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php
opt/app/vendor/symfony/http-kernel/Profiler/Profile.php opt/app/vendor/symfony/http-kernel/Profiler/Profile.php
opt/app/vendor/symfony/http-kernel/Profiler/Profiler.php opt/app/vendor/symfony/http-kernel/Profiler/Profiler.php
opt/app/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php opt/app/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php
opt/app/vendor/symfony/http-kernel/README.md opt/app/vendor/symfony/http-kernel/README.md
opt/app/vendor/symfony/http-kernel/RebootableInterface.php
opt/app/vendor/symfony/http-kernel/Resources/welcome.html.php
opt/app/vendor/symfony/http-kernel/TerminableInterface.php opt/app/vendor/symfony/http-kernel/TerminableInterface.php
opt/app/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php opt/app/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php
opt/app/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php opt/app/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php
@ -4614,11 +4712,13 @@ 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/ClientTest.php
opt/app/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php opt/app/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php
opt/app/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.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/ArgumentResolverTest.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/ContainerControllerResolverTest.php
opt/app/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php opt/app/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php
opt/app/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php opt/app/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php
opt/app/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php opt/app/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php
opt/app/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log
opt/app/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php opt/app/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php
opt/app/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php opt/app/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php
opt/app/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php opt/app/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php
@ -4634,9 +4734,13 @@ opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClasses
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php
opt/app/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php
opt/app/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php
opt/app/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php opt/app/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php opt/app/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php opt/app/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php
@ -4676,6 +4780,7 @@ opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/BasicTypesController.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/BasicTypesController.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php
@ -4694,6 +4799,7 @@ opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Extensi
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php opt/app/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt
opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt opt/app/vendor/symfony/http-kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt
@ -4716,6 +4822,7 @@ opt/app/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php
opt/app/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php opt/app/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php
opt/app/vendor/symfony/http-kernel/Tests/HttpKernelTest.php opt/app/vendor/symfony/http-kernel/Tests/HttpKernelTest.php
opt/app/vendor/symfony/http-kernel/Tests/KernelTest.php opt/app/vendor/symfony/http-kernel/Tests/KernelTest.php
opt/app/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php
opt/app/vendor/symfony/http-kernel/Tests/Logger.php opt/app/vendor/symfony/http-kernel/Tests/Logger.php
opt/app/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php opt/app/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php
opt/app/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php opt/app/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php
@ -5297,6 +5404,7 @@ opt/app/vendor/twig/twig/lib/Twig/Cache/Null.php
opt/app/vendor/twig/twig/lib/Twig/CacheInterface.php opt/app/vendor/twig/twig/lib/Twig/CacheInterface.php
opt/app/vendor/twig/twig/lib/Twig/Compiler.php opt/app/vendor/twig/twig/lib/Twig/Compiler.php
opt/app/vendor/twig/twig/lib/Twig/CompilerInterface.php opt/app/vendor/twig/twig/lib/Twig/CompilerInterface.php
opt/app/vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php
opt/app/vendor/twig/twig/lib/Twig/Environment.php opt/app/vendor/twig/twig/lib/Twig/Environment.php
opt/app/vendor/twig/twig/lib/Twig/Error.php opt/app/vendor/twig/twig/lib/Twig/Error.php
opt/app/vendor/twig/twig/lib/Twig/Error/Loader.php opt/app/vendor/twig/twig/lib/Twig/Error/Loader.php
@ -5417,6 +5525,7 @@ opt/app/vendor/twig/twig/lib/Twig/Node/SetTemp.php
opt/app/vendor/twig/twig/lib/Twig/Node/Spaceless.php opt/app/vendor/twig/twig/lib/Twig/Node/Spaceless.php
opt/app/vendor/twig/twig/lib/Twig/Node/Text.php opt/app/vendor/twig/twig/lib/Twig/Node/Text.php
opt/app/vendor/twig/twig/lib/Twig/Node/With.php opt/app/vendor/twig/twig/lib/Twig/Node/With.php
opt/app/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php
opt/app/vendor/twig/twig/lib/Twig/NodeInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeInterface.php
opt/app/vendor/twig/twig/lib/Twig/NodeOutputInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeOutputInterface.php
opt/app/vendor/twig/twig/lib/Twig/NodeTraverser.php opt/app/vendor/twig/twig/lib/Twig/NodeTraverser.php
@ -5427,6 +5536,7 @@ opt/app/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php
opt/app/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php
opt/app/vendor/twig/twig/lib/Twig/Parser.php opt/app/vendor/twig/twig/lib/Twig/Parser.php
opt/app/vendor/twig/twig/lib/Twig/ParserInterface.php opt/app/vendor/twig/twig/lib/Twig/ParserInterface.php
opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php
opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php
opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Html.php opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Html.php
opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php opt/app/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php
@ -5486,15 +5596,188 @@ opt/app/vendor/twig/twig/lib/Twig/TokenStream.php
opt/app/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php opt/app/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php
opt/app/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php opt/app/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php
opt/app/vendor/twig/twig/phpunit.xml.dist opt/app/vendor/twig/twig/phpunit.xml.dist
opt/app/vendor/twig/twig/src/Cache/CacheInterface.php
opt/app/vendor/twig/twig/src/Cache/FilesystemCache.php
opt/app/vendor/twig/twig/src/Cache/NullCache.php
opt/app/vendor/twig/twig/src/Compiler.php
opt/app/vendor/twig/twig/src/Environment.php
opt/app/vendor/twig/twig/src/Error/Error.php
opt/app/vendor/twig/twig/src/Error/LoaderError.php
opt/app/vendor/twig/twig/src/Error/RuntimeError.php
opt/app/vendor/twig/twig/src/Error/SyntaxError.php
opt/app/vendor/twig/twig/src/ExpressionParser.php
opt/app/vendor/twig/twig/src/Extension/AbstractExtension.php
opt/app/vendor/twig/twig/src/Extension/CoreExtension.php
opt/app/vendor/twig/twig/src/Extension/DebugExtension.php
opt/app/vendor/twig/twig/src/Extension/EscaperExtension.php
opt/app/vendor/twig/twig/src/Extension/ExtensionInterface.php
opt/app/vendor/twig/twig/src/Extension/GlobalsInterface.php
opt/app/vendor/twig/twig/src/Extension/InitRuntimeInterface.php
opt/app/vendor/twig/twig/src/Extension/OptimizerExtension.php
opt/app/vendor/twig/twig/src/Extension/ProfilerExtension.php
opt/app/vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php
opt/app/vendor/twig/twig/src/Extension/SandboxExtension.php
opt/app/vendor/twig/twig/src/Extension/StagingExtension.php
opt/app/vendor/twig/twig/src/Extension/StringLoaderExtension.php
opt/app/vendor/twig/twig/src/FileExtensionEscapingStrategy.php
opt/app/vendor/twig/twig/src/Lexer.php
opt/app/vendor/twig/twig/src/Loader/ArrayLoader.php
opt/app/vendor/twig/twig/src/Loader/ChainLoader.php
opt/app/vendor/twig/twig/src/Loader/ExistsLoaderInterface.php
opt/app/vendor/twig/twig/src/Loader/FilesystemLoader.php
opt/app/vendor/twig/twig/src/Loader/LoaderInterface.php
opt/app/vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php
opt/app/vendor/twig/twig/src/Markup.php
opt/app/vendor/twig/twig/src/Node/AutoEscapeNode.php
opt/app/vendor/twig/twig/src/Node/BlockNode.php
opt/app/vendor/twig/twig/src/Node/BlockReferenceNode.php
opt/app/vendor/twig/twig/src/Node/BodyNode.php
opt/app/vendor/twig/twig/src/Node/CheckSecurityNode.php
opt/app/vendor/twig/twig/src/Node/DoNode.php
opt/app/vendor/twig/twig/src/Node/EmbedNode.php
opt/app/vendor/twig/twig/src/Node/Expression/AbstractExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/ArrayExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php
opt/app/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/CallExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/ConstantExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php
opt/app/vendor/twig/twig/src/Node/Expression/FilterExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/FunctionExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/NameExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/ParentExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/TempNameExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/NullTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/OddTest.php
opt/app/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php
opt/app/vendor/twig/twig/src/Node/Expression/TestExpression.php
opt/app/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php
opt/app/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php
opt/app/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php
opt/app/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php
opt/app/vendor/twig/twig/src/Node/FlushNode.php
opt/app/vendor/twig/twig/src/Node/ForLoopNode.php
opt/app/vendor/twig/twig/src/Node/ForNode.php
opt/app/vendor/twig/twig/src/Node/IfNode.php
opt/app/vendor/twig/twig/src/Node/ImportNode.php
opt/app/vendor/twig/twig/src/Node/IncludeNode.php
opt/app/vendor/twig/twig/src/Node/MacroNode.php
opt/app/vendor/twig/twig/src/Node/ModuleNode.php
opt/app/vendor/twig/twig/src/Node/Node.php
opt/app/vendor/twig/twig/src/Node/NodeCaptureInterface.php
opt/app/vendor/twig/twig/src/Node/NodeOutputInterface.php
opt/app/vendor/twig/twig/src/Node/PrintNode.php
opt/app/vendor/twig/twig/src/Node/SandboxNode.php
opt/app/vendor/twig/twig/src/Node/SandboxedPrintNode.php
opt/app/vendor/twig/twig/src/Node/SetNode.php
opt/app/vendor/twig/twig/src/Node/SetTempNode.php
opt/app/vendor/twig/twig/src/Node/SpacelessNode.php
opt/app/vendor/twig/twig/src/Node/TextNode.php
opt/app/vendor/twig/twig/src/Node/WithNode.php
opt/app/vendor/twig/twig/src/NodeTraverser.php
opt/app/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php
opt/app/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php
opt/app/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php
opt/app/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php
opt/app/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php
opt/app/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php
opt/app/vendor/twig/twig/src/Parser.php
opt/app/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php
opt/app/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php
opt/app/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php
opt/app/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php
opt/app/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php
opt/app/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php
opt/app/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php
opt/app/vendor/twig/twig/src/Profiler/Profile.php
opt/app/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php
opt/app/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php
opt/app/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityPolicy.php
opt/app/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php
opt/app/vendor/twig/twig/src/Source.php
opt/app/vendor/twig/twig/src/Template.php
opt/app/vendor/twig/twig/src/TemplateWrapper.php
opt/app/vendor/twig/twig/src/Test/IntegrationTestCase.php
opt/app/vendor/twig/twig/src/Test/NodeTestCase.php
opt/app/vendor/twig/twig/src/Token.php
opt/app/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/BlockTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/DoTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/FilterTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/FlushTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/ForTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/FromTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/IfTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/ImportTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/MacroTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/SetTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/TokenParserInterface.php
opt/app/vendor/twig/twig/src/TokenParser/UseTokenParser.php
opt/app/vendor/twig/twig/src/TokenParser/WithTokenParser.php
opt/app/vendor/twig/twig/src/TokenStream.php
opt/app/vendor/twig/twig/src/TwigFilter.php
opt/app/vendor/twig/twig/src/TwigFunction.php
opt/app/vendor/twig/twig/src/TwigTest.php
opt/app/vendor/twig/twig/src/Util/DeprecationCollector.php
opt/app/vendor/twig/twig/src/Util/TemplateDirIterator.php
opt/app/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php opt/app/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/CompilerTest.php opt/app/vendor/twig/twig/test/Twig/Tests/CompilerTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/ContainerRuntimeLoaderTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php opt/app/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php opt/app/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/ErrorTest.php opt/app/vendor/twig/twig/test/Twig/Tests/ErrorTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php opt/app/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/FactoryRuntimeLoaderTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php opt/app/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php opt/app/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php opt/app/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php
@ -5561,6 +5844,7 @@ opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_html_attr.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_html_attr.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_javascript.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test
@ -5751,6 +6035,7 @@ opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block2.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block2.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block3.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/parent_block3.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/use_with_parent.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test
opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test opt/app/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test
@ -5851,12 +6136,11 @@ opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/BlackfireTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/HtmlTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/HtmlTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/TextTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/TextTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php opt/app/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/RuntimeFactoryLoaderTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/TemplateTest.php opt/app/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php opt/app/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php opt/app/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/Util/DeprecationCollectorTest.php
opt/app/vendor/twig/twig/test/Twig/Tests/escapingTest.php opt/app/vendor/twig/twig/test/Twig/Tests/escapingTest.php
opt/app/vendor/twig/twig/test/bootstrap.php
opt/app/vendor/vlucas/phpdotenv/LICENSE.txt opt/app/vendor/vlucas/phpdotenv/LICENSE.txt
opt/app/vendor/vlucas/phpdotenv/composer.json opt/app/vendor/vlucas/phpdotenv/composer.json
opt/app/vendor/vlucas/phpdotenv/src/Dotenv.php opt/app/vendor/vlucas/phpdotenv/src/Dotenv.php
@ -6010,6 +6294,8 @@ usr/lib/x86_64-linux-gnu/libxml2.so.2
usr/lib/x86_64-linux-gnu/libxml2.so.2.9.1 usr/lib/x86_64-linux-gnu/libxml2.so.2.9.1
usr/lib/x86_64-linux-gnu/libxslt.so.1 usr/lib/x86_64-linux-gnu/libxslt.so.1
usr/lib/x86_64-linux-gnu/libxslt.so.1.1.28 usr/lib/x86_64-linux-gnu/libxslt.so.1.1.28
usr/lib/x86_64-linux-gnu/libzip.so.5
usr/lib/x86_64-linux-gnu/libzip.so.5.0.0
usr/sbin/mysqld usr/sbin/mysqld
usr/sbin/nginx usr/sbin/nginx
usr/sbin/php-fpm7.1 usr/sbin/php-fpm7.1

View File

@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = ( manifest = (
appTitle = (defaultText = "Firefly III"), appTitle = (defaultText = "Firefly III"),
appVersion = 5, appVersion = 6,
appMarketingVersion = (defaultText = "4.6.11.1"), appMarketingVersion = (defaultText = "4.6.12"),
actions = [ actions = [
# Define your "new document" handlers here. # Define your "new document" handlers here.
@ -41,7 +41,7 @@ const pkgdef :Spk.PackageDefinition = (
market = (png = (dpi1x = embed "app-graphics/firefly-iii-150.png")) market = (png = (dpi1x = embed "app-graphics/firefly-iii-150.png"))
), ),
website = "https://firefly-iii.github.io/", website = "https://firefly-iii.org/",
codeUrl = "https://github.com/firefly-iii/firefly-iii", codeUrl = "https://github.com/firefly-iii/firefly-iii",
license = (openSource = gpl3), license = (openSource = gpl3),
# The license this package is distributed under. See # The license this package is distributed under. See

View File

@ -1,6 +1,7 @@
language: php language: php
php: php:
- 7.1 - 7.1
- 7.2
cache: cache:
directories: directories:
@ -22,7 +23,7 @@ script:
- phpunit -c phpunit.coverage.xml - phpunit -c phpunit.coverage.xml
after_success: after_success:
- travis_retry php vendor/bin/coveralls -x storage/build/clover-all.xml - travis_retry php vendor/bin/php-coveralls -x storage/build/clover-all.xml
# safelist # safelist
branches: branches:

View File

@ -2,6 +2,31 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [4.6.12] - 2017-12-31
### Added
- Support for Indonesian.
- New report, see [issue 384](https://github.com/firefly-iii/firefly-iii/issues/384)
- [Issue 964](https://github.com/firefly-iii/firefly-iii/issues/964) as suggested by [gavu](https://github.com/gavu)
### Changed
- Greatly improved Docker support and documentation.
### Fixed
- [Issue 1046](https://github.com/firefly-iii/firefly-iii/issues/1046), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1047](https://github.com/firefly-iii/firefly-iii/issues/1047), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1048](https://github.com/firefly-iii/firefly-iii/issues/1048), as reported by [webence](https://github.com/webence)
- [Issue 1049](https://github.com/firefly-iii/firefly-iii/issues/1049), as reported by [nicoschreiner](https://github.com/nicoschreiner)
- [Issue 1015](https://github.com/firefly-iii/firefly-iii/issues/1015), as reporterd by a user on Tweakers.net
- [Issue 1056](https://github.com/firefly-iii/firefly-iii/issues/1056), as reported by [repercussion](https://github.com/repercussion)
- [Issue 1061](https://github.com/firefly-iii/firefly-iii/issues/1061), as reported by [Meizikyn](https://github.com/Meizikyn)
- [Issue 1045](https://github.com/firefly-iii/firefly-iii/issues/1045), as reported by [gavu](https://github.com/gavu)
- First code for [issue 1040](https://github.com/firefly-iii/firefly-iii/issues/1040) ([simonsmiley](https://github.com/simonsmiley))
- [Issue 1059](https://github.com/firefly-iii/firefly-iii/issues/1059), as reported by [4oo4](https://github.com/4oo4)
- [Issue 1063](https://github.com/firefly-iii/firefly-iii/issues/1063), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1064](https://github.com/firefly-iii/firefly-iii/issues/1064), as reported by [pkoziol](https://github.com/pkoziol)
- [Issue 1066](https://github.com/firefly-iii/firefly-iii/issues/1066), reported by [wtercato](https://github.com/wtercato)
## [4.6.11.1] - 2017-12-08 ## [4.6.11.1] - 2017-12-08
### Added ### Added
- Import routine can scan for matching bills, [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956) - Import routine can scan for matching bills, [issue 956](https://github.com/firefly-iii/firefly-iii/issues/956)

View File

@ -1,5 +1,12 @@
# use PHP 7.1 and Apache as a base.
FROM php:7.1-apache FROM php:7.1-apache
# set working dir
ENV FIREFLY_PATH /var/www/firefly-iii
WORKDIR $FIREFLY_PATH
ADD . $FIREFLY_PATH
# install packages
RUN apt-get update -y && \ RUN apt-get update -y && \
apt-get install -y --no-install-recommends libcurl4-openssl-dev \ apt-get install -y --no-install-recommends libcurl4-openssl-dev \
zlib1g-dev \ zlib1g-dev \
@ -17,11 +24,13 @@ RUN apt-get update -y && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# 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 RUN docker-php-ext-install -j$(nproc) curl gd intl json readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 pdo_pgsql
# Generate locales supported by firefly # Generate locales supported by Firefly III
RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen RUN echo "de_DE.UTF-8 UTF-8\nen_US.UTF-8 UTF-8\nfr_FR.UTF-8 UTF-8\nid_ID.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npl_PL.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
# copy Apache config to correct spot.
COPY ./docker/apache2.conf /etc/apache2/apache2.conf COPY ./docker/apache2.conf /etc/apache2/apache2.conf
# Enable apache mod rewrite.. # Enable apache mod rewrite..
@ -30,23 +39,23 @@ RUN a2enmod rewrite
# Enable apache mod ssl.. # Enable apache mod ssl..
RUN a2enmod ssl RUN a2enmod ssl
# Create volumes for several directories:
VOLUME $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload
# Setup the Composer installer # Setup the Composer installer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Copy Apache Configs # Enable default site (Firefly III)
COPY ./docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf COPY ./docker/apache-firefly.conf /etc/apache2/sites-available/000-default.conf
ENV FIREFLY_PATH /var/www/firefly-iii # Make sure we own Firefly III directory
WORKDIR $FIREFLY_PATH
# The working directory
COPY . $FIREFLY_PATH
RUN chown -R www-data:www-data /var/www && chmod -R 775 $FIREFLY_PATH/storage RUN chown -R www-data:www-data /var/www && chmod -R 775 $FIREFLY_PATH/storage
RUN composer install --prefer-dist --no-dev --no-scripts # Run composer
RUN composer install --prefer-dist --no-dev --no-scripts --no-suggest
# Expose port 80
EXPOSE 80 EXPOSE 80
# Run entrypoint thing
ENTRYPOINT ["docker/entrypoint.sh"] ENTRYPOINT ["docker/entrypoint.sh"]

View File

@ -2,9 +2,11 @@
[![Requires PHP7.1](https://img.shields.io/badge/php-7.1-red.svg)](https://secure.php.net/downloads.php) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![License](https://img.shields.io/badge/license-GPL-lightgrey.svg)](https://www.gnu.org/licenses/gpl.html) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) [![Requires PHP7.1](https://img.shields.io/badge/php-7.1-red.svg)](https://secure.php.net/downloads.php) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![License](https://img.shields.io/badge/license-GPL-lightgrey.svg)](https://www.gnu.org/licenses/gpl.html) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![The index of Firefly III](https://i.nder.be/ccynyxy0/400)](https://i.nder.be/h327vx3y) [![The account overview of Firefly III](https://i.nder.be/g8v86y2g/400)](https://i.nder.be/hkpynqr9) [![The index of Firefly III](https://firefly-iii.org/static/screenshots/4.6.12/tiny/index.png)](https://firefly-iii.org/static/screenshots/4.6.12/index.png) [![The account overview of Firefly III](https://firefly-iii.org/static/screenshots/4.6.12/tiny/account.png)](https://firefly-iii.org/static/screenshots/4.6.12/account.png)
[![The useful financial reports of Firefly III](https://i.nder.be/cs3qx4f3/400)](https://i.nder.be/cwznmryd) [![Saving money is easy!](https://i.nder.be/gpq6ykym/400)](https://i.nder.be/gum2qf8z) [![Overview of all budgets](https://firefly-iii.org/static/screenshots/4.6.12/tiny/budget.png)](https://firefly-iii.org/static/screenshots/4.6.12/budget.png) [![Overview of a category](https://firefly-iii.org/static/screenshots/4.6.12/tiny/category.png)](https://firefly-iii.org/static/screenshots/4.6.12/category.png)
[![View of a report](https://firefly-iii.org/static/screenshots/4.6.12/tiny/report1.png)](https://firefly-iii.org/static/screenshots/4.6.12/report1.png) [![View of another report](https://firefly-iii.org/static/screenshots/4.6.12/tiny/report2.png)](https://firefly-iii.org/static/screenshots/4.6.12/report2.png)
"Firefly III" is a financial manager for your personal finances. It can help you keep track of your expenses and income. "Firefly III" is a financial manager for your personal finances. It can help you keep track of your expenses and income.
Firefly III supports the use of budgets. You can categorize and tag your transactions. Firefly III supports the use of budgets. You can categorize and tag your transactions.
@ -13,17 +15,17 @@ There are many financial reports available.
## Want to try Firefly III? ## Want to try Firefly III?
There is a **[demo site](https://firefly-iii.nder.be)** with an example financial administration already present. You can use Docker, Heroku or Sandstorm.io (see below) to quickly setup your own instance. There is a **[demo site](https://demo.firefly-iii.org)** with an example financial administration already present. You can use Docker, Heroku or Sandstorm.io (see below) to quickly setup your own instance.
## Install Firefly III ## Install Firefly III
### Using docker ### Using docker
You can use docker-compose to [set up your personal secure](https://firefly-iii.github.io/using-docker.html) Firefly III environment. Advanced users can use the Dockerfile directly. You can use docker-compose to [set up your personal secure](https://firefly-iii.org/using-docker.html) Firefly III environment. Advanced users can use the Dockerfile directly.
### Using vagrant (or other VMs) ### Using vagrant (or other VMs)
You can install Firefly III on any Linux or Windows machine. You'll need a web server (preferrably on Linux) and access to the command line. Please read the [installation guide](https://firefly-iii.github.io/using-installing.html). You can install Firefly III on any Linux or Windows machine. You'll need a web server (preferrably on Linux) and access to the command line. Please read the [installation guide](https://firefly-iii.org/using-installing.html).
### Using Heroku ### Using Heroku
@ -31,8 +33,6 @@ You can install Firefly III on any Linux or Windows machine. You'll need a web s
Register for a free Heroku account and instantly run Firefly III on your very own cloud instance. Register for a free Heroku account and instantly run Firefly III on your very own cloud instance.
_My Heroku configuration is currently broken, but I'm trying to fix it._
### Using Sandstorm.io ### Using Sandstorm.io
You can find Firefly III in [the Sandstorm.io marketplace](https://apps.sandstorm.io/app/uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70). You can run it on your own installation or on Oasis. You can find Firefly III in [the Sandstorm.io marketplace](https://apps.sandstorm.io/app/uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70). You can run it on your own installation or on Oasis.
@ -53,7 +53,7 @@ Firefly works on the principle that if you know where you're money is going, you
- If you feel you're missing something you can just ask me and I'll add it! - If you feel you're missing something you can just ask me and I'll add it!
Firefly III has become pretty awesome over the years! (but please excuse me for bragging, it's just that I'm proud of it). Firefly III has become pretty awesome over the years! (but please excuse me for bragging, it's just that I'm proud of it).
[You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/). [You can read more about Firefly III, and its features, on the website](https://firefly-iii.org/).
### Contributing ### Contributing
@ -74,8 +74,12 @@ This work [is licensed](https://github.com/firefly-iii/firefly-iii/blob/master/L
### Other stuff ### Other stuff
If you like Firefly and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!) If you like Firefly III and if it helps you save lots of money, why not send me [a dime for every dollar saved](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) (this is a joke, although the Paypal form works just fine, try it!)
If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com).
### Alternatives
If you are looking for alternatives, check out [Kickball's Awesome-Selfhosted list](https://github.com/Kickball/awesome-selfhosted) which features not only Firefly III but also noteworthy alternatives such as [Silverstrike](https://github.com/agstrike/silverstrike).
[![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Coverage Status](https://coveralls.io/repos/github/firefly-iii/firefly-iii/badge.svg?branch=master)](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master) [![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Coverage Status](https://coveralls.io/repos/github/firefly-iii/firefly-iii/badge.svg?branch=master)](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master)

View File

@ -2,7 +2,7 @@
"name": "Firefly III", "name": "Firefly III",
"description": "A free and open source personal finances manager", "description": "A free and open source personal finances manager",
"repository": "https://github.com/firefly-iii/firefly-iii", "repository": "https://github.com/firefly-iii/firefly-iii",
"website": "https://firefly-iii.github.io/", "website": "https://firefly-iii.org/",
"logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/master/public/mstile-150x150.png", "logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/master/public/mstile-150x150.png",
"keywords": [ "keywords": [
"finance", "finance",

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
use Artisan; use Artisan;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Import\Routine\ImportRoutine; use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -54,7 +55,7 @@ class CreateImport extends Command
protected $signature protected $signature
= 'firefly:create-import = 'firefly:create-import
{file : The file to import.} {file : The file to import.}
{configuration : The configuration file to use for the import/} {configuration : The configuration file to use for the import.}
{--type=csv : The file type of the import.} {--type=csv : The file type of the import.}
{--user= : The user ID that the import should import for.} {--user= : The user ID that the import should import for.}
{--token= : The user\'s access token.} {--token= : The user\'s access token.}
@ -73,6 +74,8 @@ class CreateImport extends Command
* *
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly. * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
*
* @throws FireflyException
*/ */
public function handle() public function handle()
{ {
@ -93,7 +96,7 @@ class CreateImport extends Command
return; return;
} }
$configurationData = json_decode(file_get_contents($configuration)); $configurationData = json_decode(file_get_contents($configuration), true);
if (null === $configurationData) { if (null === $configurationData) {
$this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd)); $this->error(sprintf('Firefly III cannot read the contents of configuration file "%s" (working directory: "%s").', $configuration, $cwd));
@ -114,9 +117,8 @@ class CreateImport extends Command
Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]); Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]);
$this->line('Stored import data...'); $this->line('Stored import data...');
$job->configuration = $configurationData; $jobRepository->setConfiguration($job, $configurationData);
$job->status = 'configured'; $jobRepository->updateStatus($job, 'configured');
$job->save();
$this->line('Stored configuration...'); $this->line('Stored configuration...');
if (true === $this->option('start')) { if (true === $this->option('start')) {
@ -131,18 +133,26 @@ class CreateImport extends Command
$monolog->pushHandler($handler); $monolog->pushHandler($handler);
// start the actual routine: // start the actual routine:
/** @var ImportRoutine $routine */ $type = 'csv' === $job->file_type ? 'file' : $job->file_type;
$routine = app(ImportRoutine::class); $key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job); $routine->setJob($job);
$routine->run(); $routine->run();
// give feedback. // give feedback.
/** @var MessageBag $error */ /** @var MessageBag $error */
foreach ($routine->errors as $index => $error) { foreach ($routine->getErrors() as $index => $error) {
$this->error(sprintf('Error importing line #%d: %s', $index, $error)); $this->error(sprintf('Error importing line #%d: %s', $index, $error));
} }
$this->line( $this->line(
sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines) sprintf(
'The import has finished. %d transactions have been imported out of %d records.', $routine->getJournals()->count(), $routine->getLines()
)
); );
} }
@ -166,7 +176,7 @@ class CreateImport extends Command
$configuration = $this->argument('configuration'); $configuration = $this->argument('configuration');
$user = $userRepository->find(intval($this->option('user'))); $user = $userRepository->find(intval($this->option('user')));
$cwd = getcwd(); $cwd = getcwd();
$validTypes = array_keys(config('firefly.import_formats')); $validTypes = config('import.options.file.import_formats');
$type = strtolower($this->option('type')); $type = strtolower($this->option('type'));
if (null === $user->id) { if (null === $user->id) {
$this->error(sprintf('There is no user with ID %d.', $this->option('user'))); $this->error(sprintf('There is no user with ID %d.', $this->option('user')));

View File

@ -22,8 +22,9 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Logging\CommandHandler; use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Import\Routine\ImportRoutine; use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
@ -58,6 +59,8 @@ class Import extends Command
/** /**
* Run the import routine. * Run the import routine.
*
* @throws FireflyException
*/ */
public function handle() public function handle()
{ {
@ -81,17 +84,27 @@ class Import extends Command
$handler = new CommandHandler($this); $handler = new CommandHandler($this);
$monolog->pushHandler($handler); $monolog->pushHandler($handler);
/** @var ImportRoutine $routine */ // actually start job:
$routine = app(ImportRoutine::class); $type = 'csv' === $job->file_type ? 'file' : $job->file_type;
$key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job); $routine->setJob($job);
$routine->run(); $routine->run();
/** @var MessageBag $error */ /** @var MessageBag $error */
foreach ($routine->errors as $index => $error) { foreach ($routine->getErrors() as $index => $error) {
$this->error(sprintf('Error importing line #%d: %s', $index, $error)); $this->error(sprintf('Error importing line #%d: %s', $index, $error));
} }
$this->line(sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines)); $this->line(
sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->getJournals()->count(), $routine->getLines())
);
return; return;
} }

View File

@ -26,8 +26,6 @@ use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\LimitRepetition;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
@ -75,6 +73,8 @@ class UpgradeDatabase extends Command
/** /**
* Execute the console command. * Execute the console command.
*
* @throws \Exception
*/ */
public function handle() public function handle()
{ {
@ -282,6 +282,8 @@ class UpgradeDatabase extends Command
/** /**
* Move all the journal_meta notes to their note object counter parts. * Move all the journal_meta notes to their note object counter parts.
*
* @throws \Exception
*/ */
private function migrateNotes(): void private function migrateNotes(): void
{ {

View File

@ -21,14 +21,6 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* UseEncryption.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -65,6 +57,12 @@ class UseEncryption extends Command
*/ */
public function handle() public function handle()
{ {
if (config('firefly.encryption') === true) {
$this->info('Firefly III configuration calls for encrypted data.');
}
if (config('firefly.encryption') === false) {
$this->info('Firefly III configuration calls for unencrypted data.');
}
$this->handleObjects('Account', 'name', 'encrypted'); $this->handleObjects('Account', 'name', 'encrypted');
$this->handleObjects('Bill', 'name', 'name_encrypted'); $this->handleObjects('Bill', 'name', 'name_encrypted');
$this->handleObjects('Bill', 'match', 'match_encrypted'); $this->handleObjects('Bill', 'match', 'match_encrypted');
@ -84,7 +82,7 @@ class UseEncryption extends Command
public function handleObjects(string $class, string $field, string $indicator) public function handleObjects(string $class, string $field, string $indicator)
{ {
$fqn = sprintf('FireflyIII\Models\%s', $class); $fqn = sprintf('FireflyIII\Models\%s', $class);
$encrypt = config('firefly.encryption') ? 0 : 1; $encrypt = config('firefly.encryption') === true ? 0 : 1;
$set = $fqn::where($indicator, $encrypt)->get(); $set = $fqn::where($indicator, $encrypt)->get();
foreach ($set as $entry) { foreach ($set as $entry) {

View File

@ -68,6 +68,7 @@ trait VerifiesAccessToken
} }
if (!($accessToken->data === $token)) { if (!($accessToken->data === $token)) {
Log::error(sprintf('Invalid access token for user #%d.', $userId)); Log::error(sprintf('Invalid access token for user #%d.', $userId));
Log::error(sprintf('Token given is "%s", expected "%s".', $token, $accessToken->data));
return false; return false;
} }

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
use Crypt; use Crypt;
use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@ -93,6 +94,7 @@ class VerifyDatabase extends Command
$this->repairPiggyBanks(); $this->repairPiggyBanks();
$this->createLinkTypes(); $this->createLinkTypes();
$this->createAccessTokens(); $this->createAccessTokens();
$this->fixDoubleAmounts();
} }
/** /**
@ -100,6 +102,7 @@ class VerifyDatabase extends Command
*/ */
private function createAccessTokens() private function createAccessTokens()
{ {
$count = 0;
$users = User::get(); $users = User::get();
/** @var User $user */ /** @var User $user */
foreach ($users as $user) { foreach ($users as $user) {
@ -108,8 +111,12 @@ class VerifyDatabase extends Command
$token = $user->generateAccessToken(); $token = $user->generateAccessToken();
Preferences::setForUser($user, 'access_token', $token); Preferences::setForUser($user, 'access_token', $token);
$this->line(sprintf('Generated access token for user %s', $user->email)); $this->line(sprintf('Generated access token for user %s', $user->email));
++$count;
} }
} }
if (0 === $count) {
$this->info('All access tokens OK!');
}
} }
/** /**
@ -117,6 +124,7 @@ class VerifyDatabase extends Command
*/ */
private function createLinkTypes() private function createLinkTypes()
{ {
$count = 0;
$set = [ $set = [
'Related' => ['relates to', 'relates to'], 'Related' => ['relates to', 'relates to'],
'Refund' => ['(partially) refunds', 'is (partially) refunded by'], 'Refund' => ['(partially) refunds', 'is (partially) refunded by'],
@ -130,10 +138,64 @@ class VerifyDatabase extends Command
$link->name = $name; $link->name = $name;
$link->outward = $values[0]; $link->outward = $values[0];
$link->inward = $values[1]; $link->inward = $values[1];
++$count;
} }
$link->editable = false; $link->editable = false;
$link->save(); $link->save();
} }
if (0 === $count) {
$this->info('All link types OK!');
}
}
private function fixDoubleAmounts()
{
$count = 0;
// get invalid journals
$errored = [];
$journals = DB::table('transactions')
->groupBy('transaction_journal_id')
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
/** @var stdClass $entry */
foreach ($journals as $entry) {
if (0 !== bccomp(strval($entry->the_sum), '0')) {
$errored[] = $entry->transaction_journal_id;
}
}
foreach ($errored as $journalId) {
// select and update:
$res = Transaction::whereNull('deleted_at')->where('transaction_journal_id', $journalId)->groupBy('amount')->get([DB::raw('MIN(id) as first_id')]);
$ids = $res->pluck('first_id')->toArray();
DB::table('transactions')->whereIn('id', $ids)->update(['amount' => DB::raw('amount * -1')]);
++$count;
// report about it
/** @var TransactionJournal $journal */
$journal = TransactionJournal::find($journalId);
if (is_null($journal)) {
continue;
}
if (TransactionType::OPENING_BALANCE === $journal->transactionType->type) {
$this->error(
sprintf(
'Transaction #%d was stored incorrectly. One of your asset accounts may show the wrong balance. Please visit /transactions/show/%d to verify the opening balance.',
$journalId, $journalId
)
);
}
if (TransactionType::OPENING_BALANCE !== $journal->transactionType->type) {
$this->error(
sprintf(
'Transaction #%d was stored incorrectly. Could be that the transaction shows the wrong amount. Please visit /transactions/show/%d to verify the opening balance.',
$journalId, $journalId
)
);
}
}
if (0 === $count) {
$this->info('Amount integrity OK!');
}
return;
} }
/** /**
@ -290,6 +352,7 @@ class VerifyDatabase extends Command
*/ */
private function reportJournals() private function reportJournals()
{ {
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->whereNotNull('transaction_journals.deleted_at')// USE THIS ->whereNotNull('transaction_journals.deleted_at')// USE THIS
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
@ -308,6 +371,10 @@ class VerifyDatabase extends Command
'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' . 'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' .
' Find it in the table called "transactions" and change the "deleted_at" field to: "' . $entry->journal_deleted . '"' ' Find it in the table called "transactions" and change the "deleted_at" field to: "' . $entry->journal_deleted . '"'
); );
++$count;
}
if (0 === $count) {
$this->info('No orphaned transactions!');
} }
} }
@ -316,6 +383,7 @@ class VerifyDatabase extends Command
*/ */
private function reportNoTransactions() private function reportNoTransactions()
{ {
$count = 0;
$set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->groupBy('transaction_journals.id') ->groupBy('transaction_journals.id')
->whereNull('transactions.transaction_journal_id') ->whereNull('transactions.transaction_journal_id')
@ -325,6 +393,10 @@ class VerifyDatabase extends Command
$this->error( $this->error(
'Error: Journal #' . $entry->id . ' has zero transactions. Open table "transaction_journals" and delete the entry with id #' . $entry->id 'Error: Journal #' . $entry->id . ' has zero transactions. Open table "transaction_journals" and delete the entry with id #' . $entry->id
); );
++$count;
}
if (0 === $count) {
$this->info('No orphaned journals!');
} }
} }
@ -379,6 +451,8 @@ class VerifyDatabase extends Command
$sum = strval($user->transactions()->sum('amount')); $sum = strval($user->transactions()->sum('amount'));
if (0 !== bccomp($sum, '0')) { if (0 !== bccomp($sum, '0')) {
$this->error('Error: Transactions for user #' . $user->id . ' (' . $user->email . ') are off by ' . $sum . '!'); $this->error('Error: Transactions for user #' . $user->id . ' (' . $user->email . ') are off by ' . $sum . '!');
} else {
$this->info(sprintf('Amount integrity OK for user #%d', $user->id));
} }
} }
} }

View File

@ -20,15 +20,6 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* Kernel.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Console; namespace FireflyIII\Console;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;

View File

@ -33,7 +33,13 @@ class AdminRequestedTestMessage extends Event
{ {
use SerializesModels; use SerializesModels;
/**
* @var string
*/
public $ipAddress; public $ipAddress;
/**
* @var User
*/
public $user; public $user;
/** /**

View File

@ -32,7 +32,13 @@ class RegisteredUser extends Event
{ {
use SerializesModels; use SerializesModels;
/**
* @var string
*/
public $ipAddress; public $ipAddress;
/**
* @var User
*/
public $user; public $user;
/** /**

View File

@ -32,8 +32,17 @@ class RequestedNewPassword extends Event
{ {
use SerializesModels; use SerializesModels;
/**
* @var string
*/
public $ipAddress; public $ipAddress;
/**
* @var string
*/
public $token; public $token;
/**
* @var User
*/
public $user; public $user;
/** /**

View File

@ -0,0 +1,52 @@
<?php
/**
* RequestedVersionCheckStatus.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class RequestedVersionCheckStatus
*/
class RequestedVersionCheckStatus extends Event
{
use SerializesModels;
/**
* @var User
*/
public $user;
/**
* Create a new event instance. This event is triggered when Firefly III wants to know
* what the deal is with the version checker.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
}

View File

@ -20,15 +20,6 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* Handler.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Exceptions; namespace FireflyIII\Exceptions;
use ErrorException; use ErrorException;
@ -37,6 +28,9 @@ use FireflyIII\Jobs\MailError;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Request; use Request;
/**
* Class Handler
*/
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
/** /**
@ -87,6 +81,8 @@ class Handler extends ExceptionHandler
* @param \Exception $exception * @param \Exception $exception
* *
* @return mixed|void * @return mixed|void
*
* @throws Exception
*/ */
public function report(Exception $exception) public function report(Exception $exception)
{ {

View File

@ -45,44 +45,131 @@ use FireflyIII\Models\Transaction;
final class Entry final class Entry
{ {
// @formatter:off // @formatter:off
/**
* @var int
*/
public $journal_id; public $journal_id;
/**
* @var int
*/
public $transaction_id = 0; public $transaction_id = 0;
/**
* @var string
*/
public $date; public $date;
/**
* @var string
*/
public $description; public $description;
/**
* @var string
*/
public $currency_code; public $currency_code;
/**
* @var string
*/
public $amount; public $amount;
/**
* @var string
*/
public $foreign_currency_code = ''; public $foreign_currency_code = '';
/**
* @var string
*/
public $foreign_amount = '0'; public $foreign_amount = '0';
/**
* @var string
*/
public $transaction_type; public $transaction_type;
/**
* @var string
*/
public $asset_account_id; public $asset_account_id;
/**
* @var string
*/
public $asset_account_name; public $asset_account_name;
/**
* @var string
*/
public $asset_account_iban; public $asset_account_iban;
/**
* @var string
*/
public $asset_account_bic; public $asset_account_bic;
/**
* @var string
*/
public $asset_account_number; public $asset_account_number;
/**
* @var string
*/
public $asset_currency_code; public $asset_currency_code;
/**
* @var string
*/
public $opposing_account_id; public $opposing_account_id;
/**
* @var string
*/
public $opposing_account_name; public $opposing_account_name;
/**
* @var string
*/
public $opposing_account_iban; public $opposing_account_iban;
/**
* @var string
*/
public $opposing_account_bic; public $opposing_account_bic;
/**
* @var string
*/
public $opposing_account_number; public $opposing_account_number;
/**
* @var string
*/
public $opposing_currency_code; public $opposing_currency_code;
/**
* @var string
*/
public $budget_id; public $budget_id;
/**
* @var string
*/
public $budget_name; public $budget_name;
/**
* @var string
*/
public $category_id; public $category_id;
/**
* @var string
*/
public $category_name; public $category_name;
/**
* @var string
*/
public $bill_id; public $bill_id;
/**
* @var string
*/
public $bill_name; public $bill_name;
/**
* @var string
*/
public $notes; public $notes;
/**
* @var string
*/
public $tags; public $tags;
@ -116,7 +203,7 @@ final class Entry
$entry->description = $transaction->transaction_description . '(' . $transaction->description . ')'; $entry->description = $transaction->transaction_description . '(' . $transaction->description . ')';
} }
$entry->currency_code = $transaction->transactionCurrency->code; $entry->currency_code = $transaction->transactionCurrency->code;
$entry->amount = round($transaction->transaction_amount, $transaction->transactionCurrency->decimal_places); $entry->amount = strval(round($transaction->transaction_amount, $transaction->transactionCurrency->decimal_places));
$entry->foreign_currency_code = null === $transaction->foreign_currency_id ? null : $transaction->foreignCurrency->code; $entry->foreign_currency_code = null === $transaction->foreign_currency_id ? null : $transaction->foreignCurrency->code;
$entry->foreign_amount = null === $transaction->foreign_currency_id $entry->foreign_amount = null === $transaction->foreign_currency_id
@ -129,14 +216,14 @@ final class Entry
); );
$entry->transaction_type = $transaction->transaction_type_type; $entry->transaction_type = $transaction->transaction_type_type;
$entry->asset_account_id = $transaction->account_id; $entry->asset_account_id = strval($transaction->account_id);
$entry->asset_account_name = app('steam')->tryDecrypt($transaction->account_name); $entry->asset_account_name = app('steam')->tryDecrypt($transaction->account_name);
$entry->asset_account_iban = $transaction->account_iban; $entry->asset_account_iban = $transaction->account_iban;
$entry->asset_account_number = $transaction->account_number; $entry->asset_account_number = $transaction->account_number;
$entry->asset_account_bic = $transaction->account_bic; $entry->asset_account_bic = $transaction->account_bic;
$entry->asset_currency_code = $transaction->account_currency_code; $entry->asset_currency_code = $transaction->account_currency_code;
$entry->opposing_account_id = $transaction->opposing_account_id; $entry->opposing_account_id = strval($transaction->opposing_account_id);
$entry->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name); $entry->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
$entry->opposing_account_iban = $transaction->opposing_account_iban; $entry->opposing_account_iban = $transaction->opposing_account_iban;
$entry->opposing_account_number = $transaction->opposing_account_number; $entry->opposing_account_number = $transaction->opposing_account_number;
@ -144,7 +231,7 @@ final class Entry
$entry->opposing_currency_code = $transaction->opposing_currency_code; $entry->opposing_currency_code = $transaction->opposing_currency_code;
// budget // budget
$entry->budget_id = $transaction->transaction_budget_id; $entry->budget_id = strval($transaction->transaction_budget_id);
$entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name); $entry->budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
if (null === $transaction->transaction_budget_id) { if (null === $transaction->transaction_budget_id) {
$entry->budget_id = $transaction->transaction_journal_budget_id; $entry->budget_id = $transaction->transaction_journal_budget_id;
@ -152,7 +239,7 @@ final class Entry
} }
// category // category
$entry->category_id = $transaction->transaction_category_id; $entry->category_id = strval($transaction->transaction_category_id);
$entry->category_name = app('steam')->tryDecrypt($transaction->transaction_category_name); $entry->category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
if (null === $transaction->transaction_category_id) { if (null === $transaction->transaction_category_id) {
$entry->category_id = $transaction->transaction_journal_category_id; $entry->category_id = $transaction->transaction_journal_category_id;
@ -160,7 +247,7 @@ final class Entry
} }
// budget // budget
$entry->bill_id = $transaction->bill_id; $entry->bill_id = strval($transaction->bill_id);
$entry->bill_name = app('steam')->tryDecrypt($transaction->bill_name); $entry->bill_name = app('steam')->tryDecrypt($transaction->bill_name);
$entry->tags = $transaction->tags; $entry->tags = $transaction->tags;

View File

@ -32,8 +32,8 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
use FireflyIII\Models\Note;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
@ -114,7 +114,7 @@ class ExpandedProcessor implements ProcessorInterface
$notes = $this->getNotes($ids); $notes = $this->getNotes($ids);
$tags = $this->getTags($ids); $tags = $this->getTags($ids);
/** @var array $ibans */ /** @var array $ibans */
$ibans = $this->getIbans($assetIds) + $this->getIbans($opposingIds); $ibans = array_merge($this->getIbans($assetIds), $this->getIbans($opposingIds));
$currencies = $this->getAccountCurrencies($ibans); $currencies = $this->getAccountCurrencies($ibans);
$transactions->each( $transactions->each(
function (Transaction $transaction) use ($notes, $tags, $ibans, $currencies) { function (Transaction $transaction) use ($notes, $tags, $ibans, $currencies) {
@ -173,6 +173,7 @@ class ExpandedProcessor implements ProcessorInterface
* @return bool * @return bool
* *
* @throws FireflyException * @throws FireflyException
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/ */
public function createZipFile(): bool public function createZipFile(): bool
{ {
@ -309,17 +310,16 @@ class ExpandedProcessor implements ProcessorInterface
private function getNotes(array $array): array private function getNotes(array $array): array
{ {
$array = array_unique($array); $array = array_unique($array);
$set = TransactionJournalMeta::whereIn('journal_meta.transaction_journal_id', $array) $notes = Note::where('notes.noteable_type', 'FireflyIII\\Models\\TransactionJournal')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') ->whereIn('notes.noteable_id', $array)
->where('transaction_journals.user_id', $this->job->user_id) ->get(['notes.*']);
->where('journal_meta.name', 'notes')->get(
['journal_meta.transaction_journal_id', 'journal_meta.data', 'journal_meta.id']
);
$return = []; $return = [];
/** @var TransactionJournalMeta $meta */ /** @var Note $note */
foreach ($set as $meta) { foreach ($notes as $note) {
$id = intval($meta->transaction_journal_id); if (strlen(trim(strval($note->text))) > 0) {
$return[$id] = $meta->data; $id = intval($note->noteable_id);
$return[$id] = $note->text;
}
} }
return $return; return $return;

View File

@ -24,7 +24,7 @@ namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry; use FireflyIII\Export\Entry\Entry;
use League\Csv\Writer; use League\Csv\Writer;
use SplFileObject; use Storage;
/** /**
* Class CsvExporter. * Class CsvExporter.
@ -52,6 +52,8 @@ class CsvExporter extends BasicExporter implements ExporterInterface
/** /**
* @return bool * @return bool
*
* @throws \TypeError
*/ */
public function run(): bool public function run(): bool
{ {
@ -60,7 +62,10 @@ class CsvExporter extends BasicExporter implements ExporterInterface
// necessary for CSV writer: // necessary for CSV writer:
$fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->fileName; $fullPath = storage_path('export') . DIRECTORY_SEPARATOR . $this->fileName;
$writer = Writer::createFromPath(new SplFileObject($fullPath, 'a+'), 'w');
//we create the CSV into memory
$writer = Writer::createFromPath($fullPath);
$rows = []; $rows = [];
// get field names for header row: // get field names for header row:
@ -88,5 +93,8 @@ class CsvExporter extends BasicExporter implements ExporterInterface
private function tempFile() private function tempFile()
{ {
$this->fileName = $this->job->key . '-records.csv'; $this->fileName = $this->job->key . '-records.csv';
// touch file in export directory:
$disk = Storage::disk('export');
$disk->put($this->fileName, '');
} }
} }

View File

@ -0,0 +1,146 @@
<?php
/**
* MonthReportGenerator.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Account;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use Illuminate\Support\Collection;
/**
* Class MonthReportGenerator.
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
private $accounts;
/** @var Carbon */
private $end;
/** @var Collection */
private $expense;
/** @var Carbon */
private $start;
/**
* @return string
*
* @throws \Throwable
*/
public function generate(): string
{
$accountIds = join(',', $this->accounts->pluck('id')->toArray());
$expenseIds = join(',', $this->expense->pluck('id')->toArray());
$reportType = 'account';
$preferredPeriod = $this->preferredPeriod();
return view(
'reports.account.report',
compact('accountIds', 'reportType', 'expenseIds', 'preferredPeriod')
)->with('start', $this->start)->with('end', $this->end)->render();
}
/**
* @param Collection $accounts
*
* @return ReportGeneratorInterface
*/
public function setAccounts(Collection $accounts): ReportGeneratorInterface
{
$this->accounts = $accounts;
return $this;
}
/**
* @param Collection $budgets
*
* @return ReportGeneratorInterface
*/
public function setBudgets(Collection $budgets): ReportGeneratorInterface
{
return $this;
}
/**
* @param Collection $categories
*
* @return ReportGeneratorInterface
*/
public function setCategories(Collection $categories): ReportGeneratorInterface
{
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setEndDate(Carbon $date): ReportGeneratorInterface
{
$this->end = $date;
return $this;
}
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
$this->expense = $expense;
return $this;
}
/**
* @param Carbon $date
*
* @return ReportGeneratorInterface
*/
public function setStartDate(Carbon $date): ReportGeneratorInterface
{
$this->start = $date;
return $this;
}
/**
* @param Collection $tags
*
* @return ReportGeneratorInterface
*/
public function setTags(Collection $tags): ReportGeneratorInterface
{
return $this;
}
/**
* @return string
*/
protected function preferredPeriod(): string
{
return 'day';
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* MultiYearReportGenerator.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Account;
/**
* Class MultiYearReportGenerator.
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
/**
* @return string
*/
protected function preferredPeriod(): string
{
return 'year';
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* YearReportGenerator.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Account;
/**
* Class YearReportGenerator.
*/
class YearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
/**
* @return string
*/
protected function preferredPeriod(): string
{
return 'month';
}
}

View File

@ -119,6 +119,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -128,6 +128,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -146,6 +146,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -63,6 +63,13 @@ interface ReportGeneratorInterface
*/ */
public function setEndDate(Carbon $date): ReportGeneratorInterface; public function setEndDate(Carbon $date): ReportGeneratorInterface;
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface;
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -41,6 +41,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
public function generate(): string public function generate(): string
{ {
@ -101,6 +103,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -40,6 +40,8 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
public function generate(): string public function generate(): string
{ {
@ -98,6 +100,16 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -40,6 +40,8 @@ class YearReportGenerator implements ReportGeneratorInterface
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
public function generate(): string public function generate(): string
{ {
@ -98,6 +100,16 @@ class YearReportGenerator implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -65,6 +65,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
/** /**
* @return string * @return string
* @throws \Throwable
*/ */
public function generate(): string public function generate(): string
{ {
@ -141,6 +142,16 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this; return $this;
} }
/**
* @param Collection $expense
*
* @return ReportGeneratorInterface
*/
public function setExpense(Collection $expense): ReportGeneratorInterface
{
return $this;
}
/** /**
* @param Carbon $date * @param Carbon $date
* *

View File

@ -76,6 +76,7 @@ class UserEventHandler
{ {
Log::debug('In checkSingleUserIsAdmin'); Log::debug('In checkSingleUserIsAdmin');
/** @var User $user */
$user = $event->user; $user = $event->user;
$count = User::count(); $count = User::count();
@ -86,7 +87,7 @@ class UserEventHandler
return true; return true;
} }
// user is only user but has admin role // user is only user but has admin role
if ($count === 1 && $user->hasRole('owner')) { if (1 === $count && $user->hasRole('owner')) {
Log::debug(sprintf('User #%d is only user but has role owner so all is well.', $user->id)); Log::debug(sprintf('User #%d is only user but has role owner so all is well.', $user->id));
return true; return true;

View File

@ -0,0 +1,78 @@
<?php
/**
* VersionCheckEventHandler.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
use FireflyConfig;
use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\User;
use Log;
/**
* Class VersionCheckEventHandler
*/
class VersionCheckEventHandler
{
/**
* @param RequestedVersionCheckStatus $event
*/
public function checkForUpdates(RequestedVersionCheckStatus $event)
{
// in Sandstorm, cannot check for updates:
$sandstorm = 1 === intval(getenv('SANDSTORM'));
if ($sandstorm === true) {
return;
}
/** @var User $user */
$user = $event->user;
if (!$user->hasRole('owner')) {
return;
}
$permission = FireflyConfig::get('permission_update_check', -1);
$lastCheckTime = FireflyConfig::get('last_update_check', time());
$now = time();
if ($now - $lastCheckTime->data < 604800) {
Log::debug('Checked for updates less than a week ago.');
return;
}
// last check time was more than a week ago.
Log::debug('Have not checked for a new version in a week!');
// have actual permission?
if ($permission->data === -1) {
// never asked before.
session()->flash('info', strval(trans('firefly.check_for_updates_permission', ['link' => route('admin.update-check')])));
return;
}
// actually check for update and inform the user.
}
}

View File

@ -32,8 +32,17 @@ use Illuminate\Support\Collection;
*/ */
class BalanceLine class BalanceLine
{ {
/**
*
*/
const ROLE_DEFAULTROLE = 1; const ROLE_DEFAULTROLE = 1;
/**
*
*/
const ROLE_TAGROLE = 2; const ROLE_TAGROLE = 2;
/**
*
*/
const ROLE_DIFFROLE = 3; const ROLE_DIFFROLE = 3;
/** @var Collection */ /** @var Collection */

View File

@ -41,40 +41,14 @@ class BillLine
/** @var string */ /** @var string */
protected $min; protected $min;
/** @var Carbon */ /** @var Carbon */
private $endOfPayDate;
/** @var Carbon */
private $lastHitDate; private $lastHitDate;
/** @var Carbon */ /** @var Carbon */
private $payDate; private $payDate;
/** @var Carbon */
private $endOfPayDate;
/** @var int */ /** @var int */
private $transactionJournalId; private $transactionJournalId;
/**
* @return Carbon
*/
public function getPayDate(): Carbon
{
return $this->payDate;
}
/**
* @return Carbon
*/
public function getEndOfPayDate(): Carbon
{
return $this->endOfPayDate;
}
/**
* @param Carbon $endOfPayDate
*/
public function setEndOfPayDate(Carbon $endOfPayDate): void
{
$this->endOfPayDate = $endOfPayDate;
}
/** /**
* BillLine constructor. * BillLine constructor.
*/ */
@ -115,6 +89,22 @@ class BillLine
$this->bill = $bill; $this->bill = $bill;
} }
/**
* @return Carbon
*/
public function getEndOfPayDate(): Carbon
{
return $this->endOfPayDate;
}
/**
* @param Carbon $endOfPayDate
*/
public function setEndOfPayDate(Carbon $endOfPayDate): void
{
$this->endOfPayDate = $endOfPayDate;
}
/** /**
* @return Carbon * @return Carbon
*/ */
@ -163,6 +153,22 @@ class BillLine
$this->min = $min; $this->min = $min;
} }
/**
* @return Carbon
*/
public function getPayDate(): Carbon
{
return $this->payDate;
}
/**
* @param Carbon $payDate
*/
public function setPayDate(Carbon $payDate): void
{
$this->payDate = $payDate;
}
/** /**
* @return int * @return int
*/ */
@ -202,12 +208,4 @@ class BillLine
{ {
$this->hit = $hit; $this->hit = $hit;
} }
/**
* @param Carbon $payDate
*/
public function setPayDate(Carbon $payDate): void
{
$this->payDate = $payDate;
}
} }

View File

@ -497,6 +497,20 @@ class JournalCollector implements JournalCollectorInterface
return $this; return $this;
} }
/**
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface
{
$this->withOpposingAccount();
$this->query->whereIn('opposing.account_id', $accounts->pluck('id')->toArray());
return $this;
}
/** /**
* @param int $page * @param int $page
* *

View File

@ -160,6 +160,13 @@ interface JournalCollectorInterface
*/ */
public function setOffset(int $offset): JournalCollectorInterface; public function setOffset(int $offset): JournalCollectorInterface;
/**
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface;
/** /**
* @param int $page * @param int $page
* *
@ -196,6 +203,11 @@ interface JournalCollectorInterface
*/ */
public function setTypes(array $types): JournalCollectorInterface; public function setTypes(array $types): JournalCollectorInterface;
/**
* @param User $user
*
* @return mixed
*/
public function setUser(User $user); public function setUser(User $user);
/** /**

View File

@ -37,6 +37,11 @@ class AmountFilter implements FilterInterface
/** @var int */ /** @var int */
private $modifier = 0; private $modifier = 0;
/**
* AmountFilter constructor.
*
* @param int $modifier
*/
public function __construct(int $modifier) public function __construct(int $modifier)
{ {
$this->modifier = $modifier; $this->modifier = $modifier;

View File

@ -24,6 +24,9 @@ namespace FireflyIII\Helpers\Filter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/**
* Interface FilterInterface
*/
interface FilterInterface interface FilterInterface
{ {
/** /**

View File

@ -34,6 +34,9 @@ use Route;
*/ */
class Help implements HelpInterface class Help implements HelpInterface
{ {
/**
*
*/
const CACHEKEY = 'help_%s_%s'; const CACHEKEY = 'help_%s_%s';
/** @var string */ /** @var string */
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'; protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';

View File

@ -79,7 +79,7 @@ class ReportHelper implements ReportHelperInterface
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
$expectedDates = $repository->getPayDatesInRange($bill, $start, $end); $expectedDates = $repository->getPayDatesInRange($bill, $start, $end);
foreach($expectedDates as $payDate) { foreach ($expectedDates as $payDate) {
$endOfPayPeriod = app('navigation')->endOfX($payDate, $bill->repeat_freq, null); $endOfPayPeriod = app('navigation')->endOfX($payDate, $bill->repeat_freq, null);
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);

View File

@ -37,14 +37,13 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use Session; use Session;
use View;
/** /**
* Class ReconcileController. * Class ReconcileController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class ReconcileController extends Controller class ReconcileController extends Controller
@ -59,8 +58,8 @@ class ReconcileController extends Controller
// translations: // translations:
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('mainTitleIcon', 'fa-credit-card'); app('view')->share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts')); app('view')->share('title', trans('firefly.accounts'));
return $next($request); return $next($request);
} }
@ -74,7 +73,7 @@ class ReconcileController extends Controller
*/ */
public function edit(TransactionJournal $journal) public function edit(TransactionJournal $journal)
{ {
if ($journal->transactionType->type !== TransactionType::RECONCILIATION) { if (TransactionType::RECONCILIATION !== $journal->transactionType->type) {
return redirect(route('transactions.edit', [$journal->id])); return redirect(route('transactions.edit', [$journal->id]));
} }
// view related code // view related code
@ -90,8 +89,6 @@ class ReconcileController extends Controller
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'transactions');
Session::flash('gaEventAction', 'edit-reconciliation');
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('reconcile.edit.fromUpdate')) { if (true !== session('reconcile.edit.fromUpdate')) {
@ -103,7 +100,6 @@ class ReconcileController extends Controller
'accounts.reconcile.edit', 'accounts.reconcile.edit',
compact('journal', 'subTitle') compact('journal', 'subTitle')
)->with('data', $preFilled); )->with('data', $preFilled);
} }
/** /**
@ -115,6 +111,7 @@ class ReconcileController extends Controller
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* *
* @throws FireflyException * @throws FireflyException
* @throws \Throwable
*/ */
public function overview(Request $request, Account $account, Carbon $start, Carbon $end) public function overview(Request $request, Account $account, Carbon $start, Carbon $end)
{ {
@ -151,7 +148,13 @@ class ReconcileController extends Controller
$diffCompare = bccomp($difference, '0'); $diffCompare = bccomp($difference, '0');
$return = [ $return = [
'post_uri' => $route, 'post_uri' => $route,
'html' => view('accounts.reconcile.overview', compact('account', 'start', 'diffCompare', 'difference', 'end', 'clearedIds', 'transactionIds', 'clearedAmount', 'startBalance', 'endBalance', 'amount', 'route', 'countCleared'))->render(), 'html' => view(
'accounts.reconcile.overview', compact(
'account', 'start', 'diffCompare', 'difference', 'end', 'clearedIds', 'transactionIds', 'clearedAmount',
'startBalance', 'endBalance', 'amount',
'route', 'countCleared'
)
)->render(),
]; ];
return Response::json($return); return Response::json($return);
@ -163,6 +166,8 @@ class ReconcileController extends Controller
* @param Carbon|null $end * @param Carbon|null $end
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*
* @throws FireflyException
*/ */
public function reconcile(Account $account, Carbon $start = null, Carbon $end = null) public function reconcile(Account $account, Carbon $start = null, Carbon $end = null)
{ {
@ -179,7 +184,7 @@ class ReconcileController extends Controller
$currencyId = intval($account->getMeta('currency_id')); $currencyId = intval($account->getMeta('currency_id'));
$currency = $currencyRepos->find($currencyId); $currency = $currencyRepos->find($currencyId);
if (0 === $currencyId) { if (0 === $currencyId) {
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
} }
// no start or end: // no start or end:
@ -187,11 +192,11 @@ class ReconcileController extends Controller
// get start and end // get start and end
if (null === $start && null === $end) { if (null === $start && null === $end) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
} }
if (null === $end) { if (null === $end) {
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
} }
$startDate = clone $start; $startDate = clone $start;
@ -206,7 +211,12 @@ class ReconcileController extends Controller
$overviewUri = route('accounts.reconcile.overview', [$account->id, '%start%', '%end%']); $overviewUri = route('accounts.reconcile.overview', [$account->id, '%start%', '%end%']);
$indexUri = route('accounts.reconcile', [$account->id, '%start%', '%end%']); $indexUri = route('accounts.reconcile', [$account->id, '%start%', '%end%']);
return view('accounts.reconcile.index', compact('account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance', 'transactionsUri', 'overviewUri', 'indexUri')); return view(
'accounts.reconcile.index', compact(
'account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance', 'transactionsUri',
'overviewUri', 'indexUri'
)
);
} }
/** /**
@ -217,16 +227,16 @@ class ReconcileController extends Controller
*/ */
public function show(JournalRepositoryInterface $repository, TransactionJournal $journal) public function show(JournalRepositoryInterface $repository, TransactionJournal $journal)
{ {
if ($journal->transactionType->type !== TransactionType::RECONCILIATION) { if (TransactionType::RECONCILIATION !== $journal->transactionType->type) {
return redirect(route('transactions.show', [$journal->id])); return redirect(route('transactions.show', [$journal->id]));
} }
$subTitle = trans('firefly.reconciliation') . ' "' . $journal->description . '"'; $subTitle = trans('firefly.reconciliation') . ' "' . $journal->description . '"';
// get main transaction: // get main transaction:
$transaction = $repository->getAssetTransaction($journal); $transaction = $repository->getAssetTransaction($journal);
$account = $transaction->account;
return view('accounts.reconcile.show', compact('journal', 'subTitle', 'transaction', 'account'));
return view('accounts.reconcile.show', compact('journal', 'subTitle', 'transaction'));
} }
/** /**
@ -241,7 +251,7 @@ class ReconcileController extends Controller
{ {
/** @var JournalRepositoryInterface $repository */ /** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class); $repository = app(JournalRepositoryInterface::class);
$transactions = $repository->getTransactionsById($request->get('transactions')); $transactions = $repository->getTransactionsById($request->get('transactions') ?? []);
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($transactions as $transaction) { foreach ($transactions as $transaction) {
$repository->reconcile($transaction); // mark as reconciled. $repository->reconcile($transaction); // mark as reconciled.
@ -287,6 +297,9 @@ class ReconcileController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed * @return mixed
*
* @throws \Throwable
* @throws FireflyException
*/ */
public function transactions(Account $account, Carbon $start, Carbon $end) public function transactions(Account $account, Carbon $start, Carbon $end)
{ {
@ -302,7 +315,7 @@ class ReconcileController extends Controller
$currencyId = intval($account->getMeta('currency_id')); $currencyId = intval($account->getMeta('currency_id'));
$currency = $currencyRepos->find($currencyId); $currency = $currencyRepos->find($currencyId);
if (0 === $currencyId) { if (0 === $currencyId) {
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
} }
$startBalance = round(app('steam')->balance($account, $startDate), $currency->decimal_places); $startBalance = round(app('steam')->balance($account, $startDate), $currency->decimal_places);
@ -334,10 +347,10 @@ class ReconcileController extends Controller
*/ */
public function update(ReconciliationFormRequest $request, AccountRepositoryInterface $repository, TransactionJournal $journal) public function update(ReconciliationFormRequest $request, AccountRepositoryInterface $repository, TransactionJournal $journal)
{ {
if ($journal->transactionType->type !== TransactionType::RECONCILIATION) { if (TransactionType::RECONCILIATION !== $journal->transactionType->type) {
return redirect(route('transactions.show', [$journal->id])); return redirect(route('transactions.show', [$journal->id]));
} }
if (bccomp('0', $request->get('amount')) === 0) { if (0 === bccomp('0', $request->get('amount'))) {
Session::flash('error', trans('firefly.amount_cannot_be_zero')); Session::flash('error', trans('firefly.amount_cannot_be_zero'));
return redirect(route('accounts.reconcile.edit', [$journal->id]))->withInput(); return redirect(route('accounts.reconcile.edit', [$journal->id]))->withInput();
@ -356,10 +369,8 @@ class ReconcileController extends Controller
// redirect to previous URL. // redirect to previous URL.
return redirect($this->getPreviousUri('reconcile.edit.uri')); return redirect($this->getPreviousUri('reconcile.edit.uri'));
} }
/** /**
* @param Account $account * @param Account $account
* *
@ -372,7 +383,7 @@ class ReconcileController extends Controller
/** @var Transaction $transaction */ /** @var Transaction $transaction */
$transaction = $account->transactions()->first(); $transaction = $account->transactions()->first();
if (null === $transaction) { if (null === $transaction) {
throw new FireflyException('Expected a transaction. This account has none. BEEP, error.'); throw new FireflyException(sprintf('Expected a transaction. Account #%d has none. BEEP, error.', $account->id)); // @codeCoverageIgnore
} }
$journal = $transaction->transactionJournal; $journal = $transaction->transactionJournal;

View File

@ -36,9 +36,9 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation;
use Preferences; use Preferences;
use Steam; use Steam;
use View; use View;
@ -60,8 +60,8 @@ class AccountController extends Controller
// translations: // translations:
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('mainTitleIcon', 'fa-credit-card'); app('view')->share('mainTitleIcon', 'fa-credit-card');
View::share('title', trans('firefly.accounts')); app('view')->share('title', trans('firefly.accounts'));
return $next($request); return $next($request);
} }
@ -96,20 +96,17 @@ class AccountController extends Controller
$this->rememberPreviousUri('accounts.create.uri'); $this->rememberPreviousUri('accounts.create.uri');
} }
$request->session()->forget('accounts.create.fromStore'); $request->session()->forget('accounts.create.fromStore');
$request->session()->flash('gaEventCategory', 'accounts');
$request->session()->flash('gaEventAction', 'create-' . $what);
return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencySelectList', 'allCurrencies', 'roles')); return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencySelectList', 'allCurrencies', 'roles'));
} }
/** /**
* @param Request $request
* @param AccountRepositoryInterface $repository * @param AccountRepositoryInterface $repository
* @param Account $account * @param Account $account
* *
* @return View * @return View
*/ */
public function delete(Request $request, AccountRepositoryInterface $repository, Account $account) public function delete(AccountRepositoryInterface $repository, Account $account)
{ {
$typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type); $typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
@ -118,8 +115,6 @@ class AccountController extends Controller
// put previous url in session // put previous url in session
$this->rememberPreviousUri('accounts.delete.uri'); $this->rememberPreviousUri('accounts.delete.uri');
$request->session()->flash('gaEventCategory', 'accounts');
$request->session()->flash('gaEventAction', 'delete-' . $typeName);
return view('accounts.delete', compact('account', 'subTitle', 'accountList')); return view('accounts.delete', compact('account', 'subTitle', 'accountList'));
} }
@ -156,6 +151,10 @@ class AccountController extends Controller
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* *
* @return View * @return View
*
* @throws FireflyException
* @throws FireflyException
* @throws FireflyException
*/ */
public function edit(Request $request, Account $account) public function edit(Request $request, Account $account)
{ {
@ -198,8 +197,6 @@ class AccountController extends Controller
'currency_id' => $currency->id, 'currency_id' => $currency->id,
]; ];
$request->session()->flash('preFilled', $preFilled); $request->session()->flash('preFilled', $preFilled);
$request->session()->flash('gaEventCategory', 'accounts');
$request->session()->flash('gaEventAction', 'edit-' . $what);
return view( return view(
'accounts.edit', 'accounts.edit',
@ -218,18 +215,24 @@ class AccountController extends Controller
} }
/** /**
* @param Request $request
* @param AccountRepositoryInterface $repository * @param AccountRepositoryInterface $repository
* @param string $what * @param string $what
* *
* @return View * @return View
*/ */
public function index(AccountRepositoryInterface $repository, string $what) public function index(Request $request, AccountRepositoryInterface $repository, string $what)
{ {
$what = $what ?? 'asset'; $what = $what ?? 'asset';
$subTitle = trans('firefly.' . $what . '_accounts'); $subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$types = config('firefly.accountTypesByIdentifier.' . $what); $types = config('firefly.accountTypesByIdentifier.' . $what);
$accounts = $repository->getAccountsByType($types); $collection = $repository->getAccountsByType($types);
$total = $collection->count();
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$accounts = $collection->slice(($page - 1) * $pageSize, $pageSize);
unset($collection);
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', Carbon::now()->startOfMonth()); $start = clone session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
@ -250,7 +253,11 @@ class AccountController extends Controller
} }
); );
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts')); // make paginator:
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
$accounts->setPath(route('accounts.index', [$what]));
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'page', 'accounts'));
} }
/** /**
@ -265,6 +272,8 @@ class AccountController extends Controller
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // long and complex but not that excessively so. * @SuppressWarnings(PHPMD.CyclomaticComplexity) // long and complex but not that excessively so.
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @throws FireflyException
*/ */
public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '') public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '')
{ {
@ -276,7 +285,7 @@ class AccountController extends Controller
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$chartUri = route('chart.account.single', [$account->id]); $chartUri = route('chart.account.single', [$account->id]);
$start = null; $start = null;
$end = null; $end = null;
@ -299,7 +308,7 @@ class AccountController extends Controller
// prep for "specific date" view. // prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) { if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
$fStart = $start->formatLocalized($this->monthAndDayFormat); $fStart = $start->formatLocalized($this->monthAndDayFormat);
$fEnd = $end->formatLocalized($this->monthAndDayFormat); $fEnd = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]); $subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
@ -309,8 +318,8 @@ class AccountController extends Controller
// prep for current period view // prep for current period view
if (0 === strlen($moment)) { if (0 === strlen($moment)) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$fStart = $start->formatLocalized($this->monthAndDayFormat); $fStart = $start->formatLocalized($this->monthAndDayFormat);
$fEnd = $end->formatLocalized($this->monthAndDayFormat); $fEnd = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]); $subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
@ -423,8 +432,8 @@ class AccountController extends Controller
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$start = $repository->oldestJournalDate($account); $start = $repository->oldestJournalDate($account);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = app('navigation')->startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range, null); $end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection; $entries = new Collection;
$count = 0; $count = 0;
// properties for cache // properties for cache
@ -440,8 +449,8 @@ class AccountController extends Controller
Log::debug('Going to get period expenses and incomes.'); Log::debug('Going to get period expenses and incomes.');
while ($end >= $start && $count < 90) { while ($end >= $start && $count < 90) {
$end = Navigation::startOfPeriod($end, $range); $end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range); $currentEnd = app('navigation')->endOfPeriod($end, $range);
// try a collector for income: // try a collector for income:
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
@ -455,7 +464,7 @@ class AccountController extends Controller
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount(); $collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount();
$spent = strval($collector->getJournals()->sum('transaction_amount')); $spent = strval($collector->getJournals()->sum('transaction_amount'));
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = app('navigation')->periodShow($end, $range);
$entries->push( $entries->push(
[ [
'string' => $dateStr, 'string' => $dateStr,
@ -464,7 +473,7 @@ class AccountController extends Controller
'earned' => $earned, 'earned' => $earned,
'date' => clone $end,] 'date' => clone $end,]
); );
$end = Navigation::subtractPeriod($end, $range, 1); $end = app('navigation')->subtractPeriod($end, $range, 1);
++$count; ++$count;
} }
$cache->store($entries); $cache->store($entries);

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin; namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Http\Requests\ConfigurationRequest; use FireflyIII\Http\Requests\ConfigurationRequest;
use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\FireflyConfig;
use Preferences; use Preferences;
@ -44,12 +46,14 @@ class ConfigurationController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', strval(trans('firefly.administration'))); app('view')->share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o'); app('view')->share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class);
} }
/** /**

View File

@ -24,6 +24,8 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log; use Log;
use Session; use Session;
@ -33,6 +35,16 @@ use Session;
*/ */
class HomeController extends Controller class HomeController extends Controller
{ {
/**
* ConfigurationController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class)->except(['index']);
}
/** /**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/ */

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin; namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Requests\LinkTypeFormRequest; use FireflyIII\Http\Requests\LinkTypeFormRequest;
use FireflyIII\Models\LinkType; use FireflyIII\Models\LinkType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
@ -44,12 +45,13 @@ class LinkController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', strval(trans('firefly.administration'))); app('view')->share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o'); app('view')->share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class)->except(['index', 'show']);
} }
/** /**
@ -204,6 +206,13 @@ class LinkController extends Controller
return redirect($this->getPreviousUri('link_types.create.uri')); return redirect($this->getPreviousUri('link_types.create.uri'));
} }
/**
* @param LinkTypeFormRequest $request
* @param LinkTypeRepositoryInterface $repository
* @param LinkType $linkType
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository, LinkType $linkType) public function update(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
{ {
if (!$linkType->editable) { if (!$linkType->editable) {

View File

@ -0,0 +1,142 @@
<?php
/**
* UpdateController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyConfig;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Services\Github\Object\Release;
use FireflyIII\Services\Github\Request\UpdateRequest;
use Illuminate\Http\Request;
use Log;
use Response;
use Session;
/**
* Class HomeController.
*/
class UpdateController extends Controller
{
/**
* ConfigurationController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('title', strval(trans('firefly.administration')));
app('view')->share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class)->except(['index']);
}
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Container\EntryNotFoundException
*/
public function index()
{
$subTitle = trans('firefly.update_check_title');
$subTitleIcon = 'fa-star';
$permission = app('fireflyconfig')->get('permission_update_check', -1);
$selected = $permission->data;
$options = [
'-1' => trans('firefly.updates_ask_me_later'),
'0' => trans('firefly.updates_do_not_check'),
'1' => trans('firefly.updates_enable_check'),
];
return view('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options'));
}
/**
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function post(Request $request)
{
$checkForUpdates = intval($request->get('check_for_updates'));
FireflyConfig::set('permission_update_check', $checkForUpdates);
FireflyConfig::set('last_update_check', time());
Session::flash('success', strval(trans('firefly.configuration_updated')));
return redirect(route('admin.update-check'));
}
/**
* Does a manual update check.
*/
public function updateCheck()
{
$current = config('firefly.version');
/** @var UpdateRequest $request */
$request = app(UpdateRequest::class);
$check = -2;
$first = new Release(['id' => '0', 'title' => '0', 'updated' => '2017-01-01', 'content' => '']);
$string = '';
try {
$request->call();
$releases = $request->getReleases();
// first entry should be the latest entry:
/** @var Release $first */
$first = reset($releases);
$check = version_compare($current, $first->getTitle());
FireflyConfig::set('last_update_check', time());
} catch (FireflyException $e) {
Log::error(sprintf('Could not check for updates: %s', $e->getMessage()));
}
if ($check === -2) {
$string = strval(trans('firefly.update_check_error'));
}
if ($check === -1) {
// there is a new FF version!
$string = strval(
trans(
'firefly.update_new_version_alert',
['your_version' => $current, 'new_version' => $first->getTitle(), 'date' => $first->getUpdated()->formatLocalized($this->monthAndDayFormat)]
)
);
}
if ($check === 0) {
// you are running the current version!
$string = strval(trans('firefly.update_current_version_alert', ['version' => $current]));
}
if ($check === 1) {
// you are running a newer version!
$string = strval(trans('firefly.update_newer_version_alert', ['your_version' => $current, 'new_version' => $first->getTitle()]));
}
return Response::json(['result' => $string]);
}
}

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin; namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Http\Requests\UserFormRequest; use FireflyIII\Http\Requests\UserFormRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
@ -45,12 +47,14 @@ class UserController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', strval(trans('firefly.administration'))); app('view')->share('title', strval(trans('firefly.administration')));
View::share('mainTitleIcon', 'fa-hand-spock-o'); app('view')->share('mainTitleIcon', 'fa-hand-spock-o');
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class)->except(['index', 'show']);
$this->middleware(IsSandStormUser::class);
} }
/** /**

View File

@ -50,8 +50,8 @@ class AttachmentController extends Controller
// translations: // translations:
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('mainTitleIcon', 'fa-paperclip'); app('view')->share('mainTitleIcon', 'fa-paperclip');
View::share('title', trans('firefly.attachments')); app('view')->share('title', trans('firefly.attachments'));
return $next($request); return $next($request);
} }
@ -59,19 +59,16 @@ class AttachmentController extends Controller
} }
/** /**
* @param Request $request
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return View * @return View
*/ */
public function delete(Request $request, Attachment $attachment) public function delete(Attachment $attachment)
{ {
$subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]); $subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]);
// put previous url in session // put previous url in session
$this->rememberPreviousUri('attachments.delete.uri'); $this->rememberPreviousUri('attachments.delete.uri');
$request->session()->flash('gaEventCategory', 'attachments');
$request->session()->flash('gaEventAction', 'delete-attachment');
return view('attachments.delete', compact('attachment', 'subTitle')); return view('attachments.delete', compact('attachment', 'subTitle'));
} }
@ -151,6 +148,8 @@ class AttachmentController extends Controller
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/ */
public function preview(Attachment $attachment) public function preview(Attachment $attachment)
{ {

View File

@ -20,20 +20,14 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* ForgotPasswordController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
/**
* Class ForgotPasswordController
*/
class ForgotPasswordController extends Controller class ForgotPasswordController extends Controller
{ {
/* /*

View File

@ -20,15 +20,6 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* LoginController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyConfig; use FireflyConfig;
@ -40,19 +31,16 @@ use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Schema; use Schema;
/**
* @codeCoverageIgnore
* Class LoginController
*
* This controller handles authenticating users for the application and
* redirecting them to your home screen. The controller uses a trait
* to conveniently provide its functionality to your applications.
*/
class LoginController extends Controller class LoginController extends Controller
{ {
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers; use AuthenticatesUsers;
/** /**
@ -76,7 +64,9 @@ class LoginController extends Controller
* *
* @param Request $request * @param Request $request
* *
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response|void * @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Validation\ValidationException
*/ */
public function login(Request $request) public function login(Request $request)
{ {
@ -112,7 +102,7 @@ class LoginController extends Controller
* @param Request $request * @param Request $request
* @param CookieJar $cookieJar * @param CookieJar $cookieJar
* *
* @return $this * @return $this|\Illuminate\Http\RedirectResponse
*/ */
public function logout(Request $request, CookieJar $cookieJar) public function logout(Request $request, CookieJar $cookieJar)
{ {

View File

@ -20,15 +20,6 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* RegisterController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyConfig; use FireflyConfig;
@ -40,19 +31,16 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Session; use Session;
/**
* @codeCoverageIgnore
* Class RegisterController
*
* This controller handles the registration of new users as well as their
* validation and creation. By default this controller uses a trait to
* provide this functionality without requiring any additional code.
*/
class RegisterController extends Controller class RegisterController extends Controller
{ {
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers; use RegistersUsers;
/** /**

View File

@ -20,33 +20,21 @@
*/ */
declare(strict_types=1); declare(strict_types=1);
/**
* ResetPasswordController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Foundation\Auth\ResetsPasswords;
/**
* @codeCoverageIgnore
* Class ResetPasswordController
*
* This controller is responsible for handling password reset requests
* and uses a simple trait to include this behavior. You're free to
* explore this trait and override any methods you wish to tweak.
*/
class ResetPasswordController extends Controller class ResetPasswordController extends Controller
{ {
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords; use ResetsPasswords;
/** /**

View File

@ -69,8 +69,6 @@ class TwoFactorController extends Controller
/** /**
* @return mixed * @return mixed
*
* @throws FireflyException
*/ */
public function lostTwoFactor() public function lostTwoFactor()
{ {

View File

@ -31,6 +31,7 @@ use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Preferences; use Preferences;
use URL; use URL;
@ -58,8 +59,8 @@ class BillController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.bills')); app('view')->share('title', trans('firefly.bills'));
View::share('mainTitleIcon', 'fa-calendar-o'); app('view')->share('mainTitleIcon', 'fa-calendar-o');
$this->attachments = app(AttachmentHelperInterface::class); $this->attachments = app(AttachmentHelperInterface::class);
return $next($request); return $next($request);
@ -85,24 +86,19 @@ class BillController extends Controller
$this->rememberPreviousUri('bills.create.uri'); $this->rememberPreviousUri('bills.create.uri');
} }
$request->session()->forget('bills.create.fromStore'); $request->session()->forget('bills.create.fromStore');
$request->session()->flash('gaEventCategory', 'bills');
$request->session()->flash('gaEventAction', 'create');
return view('bills.create', compact('periods', 'subTitle')); return view('bills.create', compact('periods', 'subTitle'));
} }
/** /**
* @param Request $request
* @param Bill $bill * @param Bill $bill
* *
* @return View * @return View
*/ */
public function delete(Request $request, Bill $bill) public function delete(Bill $bill)
{ {
// put previous url in session // put previous url in session
$this->rememberPreviousUri('bills.delete.uri'); $this->rememberPreviousUri('bills.delete.uri');
$request->session()->flash('gaEventCategory', 'bills');
$request->session()->flash('gaEventAction', 'delete');
$subTitle = trans('firefly.delete_bill', ['name' => $bill->name]); $subTitle = trans('firefly.delete_bill', ['name' => $bill->name]);
return view('bills.delete', compact('bill', 'subTitle')); return view('bills.delete', compact('bill', 'subTitle'));
@ -162,8 +158,6 @@ class BillController extends Controller
$request->session()->flash('preFilled', $preFilled); $request->session()->flash('preFilled', $preFilled);
$request->session()->forget('bills.edit.fromUpdate'); $request->session()->forget('bills.edit.fromUpdate');
$request->session()->flash('gaEventCategory', 'bills');
$request->session()->flash('gaEventAction', 'edit');
return view('bills.edit', compact('subTitle', 'periods', 'bill')); return view('bills.edit', compact('subTitle', 'periods', 'bill'));
} }
@ -173,15 +167,19 @@ class BillController extends Controller
* *
* @return View * @return View
*/ */
public function index(BillRepositoryInterface $repository) public function index(Request $request, BillRepositoryInterface $repository)
{ {
/** @var Carbon $start */ /** @var Carbon $start */
$start = session('start'); $start = session('start');
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end'); $end = session('end');
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$collection = $repository->getBills();
$total = $collection->count();
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
$bills = $repository->getBills(); $collection->each(
$bills->each(
function (Bill $bill) use ($repository, $start, $end) { function (Bill $bill) use ($repository, $start, $end) {
// paid in this period? // paid in this period?
$bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end); $bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end);
@ -194,6 +192,9 @@ class BillController extends Controller
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastPaidDate); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastPaidDate);
} }
); );
// paginate bills
$bills = new LengthAwarePaginator($collection, $total, $pageSize, $page);
$bills->setPath(route('bills.index'));
return view('bills.index', compact('bills')); return view('bills.index', compact('bills'));
} }
@ -240,7 +241,7 @@ class BillController extends Controller
$end = session('end'); $end = session('end');
$year = $date->year; $year = $date->year;
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$yearAverage = $repository->getYearAverage($bill, $date); $yearAverage = $repository->getYearAverage($bill, $date);
$overallAverage = $repository->getOverallAverage($bill); $overallAverage = $repository->getOverallAverage($bill);
@ -252,7 +253,6 @@ class BillController extends Controller
$transactions = $collector->getPaginatedJournals(); $transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('bills.show', [$bill->id])); $transactions->setPath(route('bills.show', [$bill->id]));
$bill->paidDates = $repository->getPaidDatesInRange($bill, $date, $end); $bill->paidDates = $repository->getPaidDatesInRange($bill, $date, $end);
$bill->payDates = $repository->getPayDatesInRange($bill, $date, $end); $bill->payDates = $repository->getPayDatesInRange($bill, $date, $end);
$lastPaidDate = $this->lastPaidDate($repository->getPaidDatesInRange($bill, $date, $end), $date); $lastPaidDate = $this->lastPaidDate($repository->getPaidDatesInRange($bill, $date, $end), $date);
@ -280,14 +280,13 @@ class BillController extends Controller
$request->session()->flash('success', strval(trans('firefly.stored_new_bill', ['name' => $bill->name]))); $request->session()->flash('success', strval(trans('firefly.stored_new_bill', ['name' => $bill->name])));
Preferences::mark(); Preferences::mark();
/** @var array $files */ /** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null; $files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$this->attachments->saveAttachmentsForModel($bill, $files); $this->attachments->saveAttachmentsForModel($bill, $files);
// flash messages // flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) { if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); $request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
} }
if (1 === intval($request->get('create_another'))) { if (1 === intval($request->get('create_another'))) {
@ -323,7 +322,7 @@ class BillController extends Controller
// flash messages // flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) { if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); $request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
} }
if (1 === intval($request->get('return_to_edit'))) { if (1 === intval($request->get('return_to_edit'))) {
@ -347,8 +346,8 @@ class BillController extends Controller
*/ */
private function lastPaidDate(Collection $dates, Carbon $default): Carbon private function lastPaidDate(Collection $dates, Carbon $default): Carbon
{ {
if ($dates->count() === 0) { if (0 === $dates->count()) {
return $default; return $default; // @codeCoverageIgnore
} }
$latest = $dates->first(); $latest = $dates->first();
/** @var Carbon $date */ /** @var Carbon $date */
@ -359,6 +358,5 @@ class BillController extends Controller
} }
return $latest; return $latest;
} }
} }

View File

@ -35,9 +35,9 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use View; use View;
@ -64,8 +64,8 @@ class BudgetController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.budgets')); app('view')->share('title', trans('firefly.budgets'));
View::share('mainTitleIcon', 'fa-tasks'); app('view')->share('mainTitleIcon', 'fa-tasks');
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
return $next($request); return $next($request);
@ -75,6 +75,7 @@ class BudgetController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param BudgetRepositoryInterface $repository
* @param Budget $budget * @param Budget $budget
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
@ -85,7 +86,7 @@ class BudgetController extends Controller
$start = Carbon::createFromFormat('Y-m-d', $request->get('start')); $start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end')); $end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount); $budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
if (bccomp($amount,'0') === 0) { if (0 === bccomp($amount, '0')) {
$budgetLimit = null; $budgetLimit = null;
} }
@ -111,27 +112,22 @@ class BudgetController extends Controller
$this->rememberPreviousUri('budgets.create.uri'); $this->rememberPreviousUri('budgets.create.uri');
} }
$request->session()->forget('budgets.create.fromStore'); $request->session()->forget('budgets.create.fromStore');
$request->session()->flash('gaEventCategory', 'budgets');
$request->session()->flash('gaEventAction', 'create');
$subTitle = (string)trans('firefly.create_new_budget'); $subTitle = (string)trans('firefly.create_new_budget');
return view('budgets.create', compact('subTitle')); return view('budgets.create', compact('subTitle'));
} }
/** /**
* @param Request $request
* @param Budget $budget * @param Budget $budget
* *
* @return View * @return View
*/ */
public function delete(Request $request, Budget $budget) public function delete(Budget $budget)
{ {
$subTitle = trans('firefly.delete_budget', ['name' => $budget->name]); $subTitle = trans('firefly.delete_budget', ['name' => $budget->name]);
// put previous url in session // put previous url in session
$this->rememberPreviousUri('budgets.delete.uri'); $this->rememberPreviousUri('budgets.delete.uri');
$request->session()->flash('gaEventCategory', 'budgets');
$request->session()->flash('gaEventAction', 'delete');
return view('budgets.delete', compact('budget', 'subTitle')); return view('budgets.delete', compact('budget', 'subTitle'));
} }
@ -167,8 +163,6 @@ class BudgetController extends Controller
$this->rememberPreviousUri('budgets.edit.uri'); $this->rememberPreviousUri('budgets.edit.uri');
} }
$request->session()->forget('budgets.edit.fromUpdate'); $request->session()->forget('budgets.edit.fromUpdate');
$request->session()->flash('gaEventCategory', 'budgets');
$request->session()->flash('gaEventAction', 'edit');
return view('budgets.edit', compact('budget', 'subTitle')); return view('budgets.edit', compact('budget', 'subTitle'));
} }
@ -181,17 +175,19 @@ class BudgetController extends Controller
* @SuppressWarnings(PHPMD.CyclomaticComplexity) complex because of while loop * @SuppressWarnings(PHPMD.CyclomaticComplexity) complex because of while loop
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function index(string $moment = null) public function index(Request $request, string $moment = null)
{ {
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = session('start', new Carbon); $start = session('start', new Carbon);
$end = session('end', new Carbon); $end = session('end', new Carbon);
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
// make date if present: // make date if present:
if (null !== $moment || 0 !== strlen(strval($moment))) { if (null !== $moment || 0 !== strlen(strval($moment))) {
try { try {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
} catch (Exception $e) { } catch (Exception $e) {
// start and end are already defined. // start and end are already defined.
} }
@ -200,9 +196,11 @@ class BudgetController extends Controller
$next->addDay(); $next->addDay();
$prev = clone $start; $prev = clone $start;
$prev->subDay(); $prev->subDay();
$prev = Navigation::startOfPeriod($prev, $range); $prev = app('navigation')->startOfPeriod($prev, $range);
$this->repository->cleanupBudgets(); $this->repository->cleanupBudgets();
$budgets = $this->repository->getActiveBudgets(); $budgets = $this->repository->getActiveBudgets();
$total = $budgets->count();
$budgets = $budgets->slice(($page - 1) * $pageSize, $pageSize);
$inactive = $this->repository->getInactiveBudgets(); $inactive = $this->repository->getInactiveBudgets();
$periodStart = $start->formatLocalized($this->monthAndDayFormat); $periodStart = $start->formatLocalized($this->monthAndDayFormat);
$periodEnd = $end->formatLocalized($this->monthAndDayFormat); $periodEnd = $end->formatLocalized($this->monthAndDayFormat);
@ -212,15 +210,19 @@ class BudgetController extends Controller
$spent = array_sum(array_column($budgetInformation, 'spent')); $spent = array_sum(array_column($budgetInformation, 'spent'));
$budgeted = array_sum(array_column($budgetInformation, 'budgeted')); $budgeted = array_sum(array_column($budgetInformation, 'budgeted'));
// paginate budgets
$budgets = new LengthAwarePaginator($budgets, $total, $pageSize, $page);
$budgets->setPath(route('budgets.index'));
// select thing for last 12 periods: // select thing for last 12 periods:
$previousLoop = []; $previousLoop = [];
$previousDate = clone $start; $previousDate = clone $start;
$count = 0; $count = 0;
while ($count < 12) { while ($count < 12) {
$previousDate->subDay(); $previousDate->subDay();
$previousDate = Navigation::startOfPeriod($previousDate, $range); $previousDate = app('navigation')->startOfPeriod($previousDate, $range);
$format = $previousDate->format('Y-m-d'); $format = $previousDate->format('Y-m-d');
$previousLoop[$format] = Navigation::periodShow($previousDate, $range); $previousLoop[$format] = app('navigation')->periodShow($previousDate, $range);
++$count; ++$count;
} }
@ -232,16 +234,16 @@ class BudgetController extends Controller
while ($count < 12) { while ($count < 12) {
$format = $nextDate->format('Y-m-d'); $format = $nextDate->format('Y-m-d');
$nextLoop[$format] = Navigation::periodShow($nextDate, $range); $nextLoop[$format] = app('navigation')->periodShow($nextDate, $range);
$nextDate = Navigation::endOfPeriod($nextDate, $range); $nextDate = app('navigation')->endOfPeriod($nextDate, $range);
++$count; ++$count;
$nextDate->addDay(); $nextDate->addDay();
} }
// display info // display info
$currentMonth = Navigation::periodShow($start, $range); $currentMonth = app('navigation')->periodShow($start, $range);
$nextText = Navigation::periodShow($next, $range); $nextText = app('navigation')->periodShow($next, $range);
$prevText = Navigation::periodShow($prev, $range); $prevText = app('navigation')->periodShow($prev, $range);
return view( return view(
'budgets.index', 'budgets.index',
@ -254,6 +256,7 @@ class BudgetController extends Controller
'prevText', 'prevText',
'periodStart', 'periodStart',
'periodEnd', 'periodEnd',
'page',
'budgetInformation', 'budgetInformation',
'inactive', 'inactive',
'budgets', 'budgets',
@ -282,10 +285,15 @@ class BudgetController extends Controller
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('info-income'); $cache->addProperty('info-income');
Log::debug(sprintf('infoIncome start is %s', $start->format('Y-m-d')));
Log::debug(sprintf('infoIncome end is %s', $end->format('Y-m-d')));
if ($cache->has()) { if ($cache->has()) {
$result = $cache->get(); // @codeCoverageIgnore // @codeCoverageIgnoreStart
$result = $cache->get();
return view('budgets.info', compact('result', 'begin', 'currentEnd')); return view('budgets.info', compact('result', 'begin', 'currentEnd'));
// @codeCoverageIgnoreEnd
} }
$result = [ $result = [
'available' => '0', 'available' => '0',
@ -294,18 +302,27 @@ class BudgetController extends Controller
]; ];
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$begin = Navigation::subtractPeriod($start, $range, 3); $begin = app('navigation')->subtractPeriod($start, $range, 3);
Log::debug(sprintf('Range is %s', $range));
Log::debug(sprintf('infoIncome begin is %s', $begin->format('Y-m-d')));
// get average amount available. // get average amount available.
$total = '0'; $total = '0';
$count = 0; $count = 0;
$currentStart = clone $begin; $currentStart = clone $begin;
while ($currentStart < $start) { while ($currentStart < $start) {
$currentEnd = Navigation::endOfPeriod($currentStart, $range); Log::debug(sprintf('Loop: currentStart is %s', $currentStart->format('Y-m-d')));
$currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
$total = bcadd($total, $this->repository->getAvailableBudget($currency, $currentStart, $currentEnd)); $total = bcadd($total, $this->repository->getAvailableBudget($currency, $currentStart, $currentEnd));
$currentStart = Navigation::addPeriod($currentStart, $range, 0); $currentStart = app('navigation')->addPeriod($currentStart, $range, 0);
++$count; ++$count;
} }
Log::debug('Loop end');
if (0 === $count) {
$count = 1;
}
$result['available'] = bcdiv($total, strval($count)); $result['available'] = bcdiv($total, strval($count));
// amount earned in this period: // amount earned in this period:
@ -360,7 +377,7 @@ class BudgetController extends Controller
// prep for "specific date" view. // prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) { if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans( $subTitle = trans(
'firefly.without_budget_between', 'firefly.without_budget_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -370,8 +387,8 @@ class BudgetController extends Controller
// prep for current period // prep for current period
if (0 === strlen($moment)) { if (0 === strlen($moment)) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview(); $periods = $this->getPeriodOverview();
$subTitle = trans( $subTitle = trans(
'firefly.without_budget_between', 'firefly.without_budget_between',
@ -380,7 +397,7 @@ class BudgetController extends Controller
} }
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
@ -422,7 +439,7 @@ class BudgetController extends Controller
$start = session('first', Carbon::create()->startOfYear()); $start = session('first', Carbon::create()->startOfYear());
$end = new Carbon; $end = new Carbon;
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$limits = $this->getLimits($budget, $start, $end); $limits = $this->getLimits($budget, $start, $end);
$repetition = null; $repetition = null;
// collector: // collector:
@ -453,7 +470,7 @@ class BudgetController extends Controller
} }
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$subTitle = trans( $subTitle = trans(
'firefly.budget_in_period', 'firefly.budget_in_period',
[ [
@ -583,8 +600,8 @@ class BudgetController extends Controller
$first = $repository->first(); $first = $repository->first();
$start = $first->date ?? new Carbon; $start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = app('navigation')->startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range, null); $end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection; $entries = new Collection;
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($start); $cache->addProperty($start);
@ -597,8 +614,8 @@ class BudgetController extends Controller
Log::debug('Going to get period expenses and incomes.'); Log::debug('Going to get period expenses and incomes.');
while ($end >= $start) { while ($end >= $start) {
$end = Navigation::startOfPeriod($end, $range); $end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range); $currentEnd = app('navigation')->endOfPeriod($end, $range);
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class); $collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]); $collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
@ -606,9 +623,9 @@ class BudgetController extends Controller
$sum = strval($set->sum('transaction_amount') ?? '0'); $sum = strval($set->sum('transaction_amount') ?? '0');
$journals = $set->count(); $journals = $set->count();
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = app('navigation')->periodShow($end, $range);
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $end]); $entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $end]);
$end = Navigation::subtractPeriod($end, $range, 1); $end = app('navigation')->subtractPeriod($end, $range, 1);
} }
$cache->store($entries); $cache->store($entries);

View File

@ -34,9 +34,9 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation;
use Preferences; use Preferences;
use Steam; use Steam;
use View; use View;
@ -55,8 +55,8 @@ class CategoryController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.categories')); app('view')->share('title', trans('firefly.categories'));
View::share('mainTitleIcon', 'fa-bar-chart'); app('view')->share('mainTitleIcon', 'fa-bar-chart');
return $next($request); return $next($request);
} }
@ -74,27 +74,22 @@ class CategoryController extends Controller
$this->rememberPreviousUri('categories.create.uri'); $this->rememberPreviousUri('categories.create.uri');
} }
$request->session()->forget('categories.create.fromStore'); $request->session()->forget('categories.create.fromStore');
$request->session()->flash('gaEventCategory', 'categories');
$request->session()->flash('gaEventAction', 'create');
$subTitle = trans('firefly.create_new_category'); $subTitle = trans('firefly.create_new_category');
return view('categories.create', compact('subTitle')); return view('categories.create', compact('subTitle'));
} }
/** /**
* @param Request $request
* @param Category $category * @param Category $category
* *
* @return View * @return View
*/ */
public function delete(Request $request, Category $category) public function delete(Category $category)
{ {
$subTitle = trans('firefly.delete_category', ['name' => $category->name]); $subTitle = trans('firefly.delete_category', ['name' => $category->name]);
// put previous url in session // put previous url in session
$this->rememberPreviousUri('categories.delete.uri'); $this->rememberPreviousUri('categories.delete.uri');
$request->session()->flash('gaEventCategory', 'categories');
$request->session()->flash('gaEventAction', 'delete');
return view('categories.delete', compact('category', 'subTitle')); return view('categories.delete', compact('category', 'subTitle'));
} }
@ -132,8 +127,6 @@ class CategoryController extends Controller
$this->rememberPreviousUri('categories.edit.uri'); $this->rememberPreviousUri('categories.edit.uri');
} }
$request->session()->forget('categories.edit.fromUpdate'); $request->session()->forget('categories.edit.fromUpdate');
$request->session()->flash('gaEventCategory', 'categories');
$request->session()->flash('gaEventAction', 'edit');
return view('categories.edit', compact('category', 'subTitle')); return view('categories.edit', compact('category', 'subTitle'));
} }
@ -143,16 +136,24 @@ class CategoryController extends Controller
* *
* @return View * @return View
*/ */
public function index(CategoryRepositoryInterface $repository) public function index(Request $request, CategoryRepositoryInterface $repository)
{ {
$categories = $repository->getCategories(); $page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$collection = $repository->getCategories();
$total = $collection->count();
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
$categories->each( $collection->each(
function (Category $category) use ($repository) { function (Category $category) use ($repository) {
$category->lastActivity = $repository->lastUseDate($category, new Collection); $category->lastActivity = $repository->lastUseDate($category, new Collection);
} }
); );
// paginate categories
$categories = new LengthAwarePaginator($collection, $total, $pageSize, $page);
$categories->setPath(route('categories.index'));
return view('categories.index', compact('categories')); return view('categories.index', compact('categories'));
} }
@ -171,7 +172,7 @@ class CategoryController extends Controller
$end = null; $end = null;
$periods = new Collection; $periods = new Collection;
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
// prep for "all" view. // prep for "all" view.
if ('all' === $moment) { if ('all' === $moment) {
@ -184,7 +185,7 @@ class CategoryController extends Controller
// prep for "specific date" view. // prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) { if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans( $subTitle = trans(
'firefly.without_category_between', 'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@ -194,8 +195,8 @@ class CategoryController extends Controller
// prep for current period // prep for current period
if (0 === strlen($moment)) { if (0 === strlen($moment)) {
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getNoCategoryPeriodOverview(); $periods = $this->getNoCategoryPeriodOverview();
$subTitle = trans( $subTitle = trans(
'firefly.without_category_between', 'firefly.without_category_between',
@ -228,11 +229,12 @@ class CategoryController extends Controller
$subTitle = $category->name; $subTitle = $category->name;
$subTitleIcon = 'fa-bar-chart'; $subTitleIcon = 'fa-bar-chart';
$page = intval($request->get('page')); $page = intval($request->get('page'));
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = null; $start = null;
$end = null; $end = null;
$periods = new Collection; $periods = new Collection;
$path = route('categories.show', [$category->id]);
// prep for "all" view. // prep for "all" view.
if ('all' === $moment) { if ('all' === $moment) {
@ -241,26 +243,28 @@ class CategoryController extends Controller
/** @var Carbon $start */ /** @var Carbon $start */
$start = null === $first ? new Carbon : $first; $start = null === $first ? new Carbon : $first;
$end = new Carbon; $end = new Carbon;
$path = route('categories.show', [$category->id, 'all']);
} }
// prep for "specific date" view. // prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) { if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment); $start = new Carbon($moment);
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans( $subTitle = trans(
'firefly.journals_in_period_for_category', 'firefly.journals_in_period_for_category',
['name' => $category->name, ['name' => $category->name,
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),] 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),]
); );
$periods = $this->getPeriodOverview($category); $periods = $this->getPeriodOverview($category);
$path = route('categories.show', [$category->id, $moment]);
} }
// prep for current period // prep for current period
if (0 === strlen($moment)) { if (0 === strlen($moment)) {
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview($category); $periods = $this->getPeriodOverview($category);
$subTitle = trans( $subTitle = trans(
'firefly.journals_in_period_for_category', 'firefly.journals_in_period_for_category',
@ -275,7 +279,7 @@ class CategoryController extends Controller
->setCategory($category)->withBudgetInformation()->withCategoryInformation(); ->setCategory($category)->withBudgetInformation()->withCategoryInformation();
$collector->removeFilter(InternalTransferFilter::class); $collector->removeFilter(InternalTransferFilter::class);
$transactions = $collector->getPaginatedJournals(); $transactions = $collector->getPaginatedJournals();
$transactions->setPath(route('categories.show', [$category->id])); $transactions->setPath($path);
return view('categories.show', compact('category', 'moment', 'transactions', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); return view('categories.show', compact('category', 'moment', 'transactions', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
} }
@ -340,8 +344,8 @@ class CategoryController extends Controller
$first = $repository->first(); $first = $repository->first();
$start = $first->date ?? new Carbon; $start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = app('navigation')->startOfPeriod($start, $range);
$end = Navigation::endOfX(new Carbon, $range, null); $end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection; $entries = new Collection;
// properties for cache // properties for cache
@ -357,8 +361,8 @@ class CategoryController extends Controller
Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Going to get period expenses and incomes between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
while ($end >= $start) { while ($end >= $start) {
Log::debug('Loop!'); Log::debug('Loop!');
$end = Navigation::startOfPeriod($end, $range); $end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range); $currentEnd = app('navigation')->endOfPeriod($end, $range);
// count journals without category in this period: // count journals without category in this period:
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
@ -389,7 +393,7 @@ class CategoryController extends Controller
$earned = $collector->getJournals()->sum('transaction_amount'); $earned = $collector->getJournals()->sum('transaction_amount');
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = app('navigation')->periodShow($end, $range);
$entries->push( $entries->push(
[ [
'string' => $dateStr, 'string' => $dateStr,
@ -401,7 +405,7 @@ class CategoryController extends Controller
'date' => clone $end, 'date' => clone $end,
] ]
); );
$end = Navigation::subtractPeriod($end, $range, 1); $end = app('navigation')->subtractPeriod($end, $range, 1);
} }
Log::debug('End of loops'); Log::debug('End of loops');
$cache->store($entries); $cache->store($entries);
@ -423,11 +427,11 @@ class CategoryController extends Controller
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$first = $repository->firstUseDate($category); $first = $repository->firstUseDate($category);
if (null === $first) { if (null === $first) {
$first = new Carbon; $first = new Carbon; // @codeCoverageIgnore
} }
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$first = Navigation::startOfPeriod($first, $range); $first = app('navigation')->startOfPeriod($first, $range);
$end = Navigation::endOfX(new Carbon, $range, null); $end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection; $entries = new Collection;
$count = 0; $count = 0;
@ -442,12 +446,12 @@ class CategoryController extends Controller
return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
while ($end >= $first && $count < 90) { while ($end >= $first && $count < 90) {
$end = Navigation::startOfPeriod($end, $range); $end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = Navigation::endOfPeriod($end, $range); $currentEnd = app('navigation')->endOfPeriod($end, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$dateStr = $end->format('Y-m-d'); $dateStr = $end->format('Y-m-d');
$dateName = Navigation::periodShow($end, $range); $dateName = app('navigation')->periodShow($end, $range);
// amount transferred // amount transferred
/** @var JournalCollectorInterface $collector */ /** @var JournalCollectorInterface $collector */
@ -468,7 +472,7 @@ class CategoryController extends Controller
'date' => clone $end, 'date' => clone $end,
] ]
); );
$end = Navigation::subtractPeriod($end, $range, 1); $end = app('navigation')->subtractPeriod($end, $range, 1);
++$count; ++$count;
} }
$cache->store($entries); $cache->store($entries);

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart; namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
@ -38,7 +37,6 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use Steam; use Steam;
@ -342,13 +340,11 @@ class AccountController extends Controller
* @param Carbon $start * @param Carbon $start
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*
* @throws FireflyException
*/ */
public function period(Account $account, Carbon $start) public function period(Account $account, Carbon $start)
{ {
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$end = Navigation::endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);

View File

@ -37,7 +37,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
use Steam; use Steam;
@ -81,7 +80,7 @@ class BudgetController extends Controller
{ {
$first = $this->repository->firstUseDate($budget); $first = $this->repository->firstUseDate($budget);
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$currentStart = Navigation::startOfPeriod($first, $range); $currentStart = app('navigation')->startOfPeriod($first, $range);
$last = session('end', new Carbon); $last = session('end', new Carbon);
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($first); $cache->addProperty($first);
@ -95,15 +94,15 @@ class BudgetController extends Controller
$final = clone $last; $final = clone $last;
$final->addYears(2); $final->addYears(2);
$budgetCollection = new Collection([$budget]); $budgetCollection = new Collection([$budget]);
$last = Navigation::endOfX($last, $range, $final); // not to overshoot. $last = app('navigation')->endOfX($last, $range, $final); // not to overshoot.
$entries = []; $entries = [];
while ($currentStart < $last) { while ($currentStart < $last) {
// periodspecific dates: // periodspecific dates:
$currentEnd = Navigation::endOfPeriod($currentStart, $range); $currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
// sub another day because reasons. // sub another day because reasons.
$currentEnd->subDay(); $currentEnd->subDay();
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd); $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$format = Navigation::periodShow($currentStart, $range); $format = app('navigation')->periodShow($currentStart, $range);
$entries[$format] = bcmul($spent, '-1'); $entries[$format] = bcmul($spent, '-1');
$currentStart = clone $currentEnd; $currentStart = clone $currentEnd;
$currentStart->addDays(2); $currentStart->addDays(2);
@ -374,7 +373,7 @@ class BudgetController extends Controller
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses $entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses
$budgeted = $this->getBudgetedInPeriod($budget, $start, $end); $budgeted = $this->getBudgetedInPeriod($budget, $start, $end);
@ -417,7 +416,7 @@ class BudgetController extends Controller
} }
// the expenses: // the expenses:
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end); $entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end);
$chartData = []; $chartData = [];
@ -464,13 +463,13 @@ class BudgetController extends Controller
*/ */
private function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array private function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array
{ {
$key = Navigation::preferredCarbonFormat($start, $end); $key = app('navigation')->preferredCarbonFormat($start, $end);
$range = Navigation::preferredRangeFormat($start, $end); $range = app('navigation')->preferredRangeFormat($start, $end);
$current = clone $start; $current = clone $start;
$budgeted = []; $budgeted = [];
while ($current < $end) { while ($current < $end) {
$currentStart = Navigation::startOfPeriod($current, $range); $currentStart = app('navigation')->startOfPeriod($current, $range);
$currentEnd = Navigation::endOfPeriod($current, $range); $currentEnd = app('navigation')->endOfPeriod($current, $range);
$budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd); $budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd);
$index = $currentStart->format($key); $index = $currentStart->format($key);
$budgeted[$index] = $budgetLimits->sum('amount'); $budgeted[$index] = $budgetLimits->sum('amount');

View File

@ -37,7 +37,6 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Response; use Response;
/** /**
@ -135,8 +134,8 @@ class BudgetReportController extends Controller
if ($cache->has()) { if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$format = Navigation::preferredCarbonLocalizedFormat($start, $end); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end); $function = app('navigation')->preferredEndOfPeriod($start, $end);
$chartData = []; $chartData = [];
$currentStart = clone $start; $currentStart = clone $start;

View File

@ -31,7 +31,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Preferences; use Preferences;
use Response; use Response;
@ -74,11 +73,11 @@ class CategoryController extends Controller
$start = $repository->firstUseDate($category); $start = $repository->firstUseDate($category);
if (null === $start) { if (null === $start) {
$start = new Carbon; $start = new Carbon; // @codeCoverageIgnore
} }
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($start, $range); $start = app('navigation')->startOfPeriod($start, $range);
$end = new Carbon; $end = new Carbon;
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$chartData = [ $chartData = [
@ -101,15 +100,15 @@ class CategoryController extends Controller
]; ];
while ($start <= $end) { while ($start <= $end) {
$currentEnd = Navigation::endOfPeriod($start, $range); $currentEnd = app('navigation')->endOfPeriod($start, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd);
$sum = bcadd($spent, $earned); $sum = bcadd($spent, $earned);
$label = Navigation::periodShow($start, $range); $label = app('navigation')->periodShow($start, $range);
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12); $chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
$chartData[1]['entries'][$label] = round($earned, 12); $chartData[1]['entries'][$label] = round($earned, 12);
$chartData[2]['entries'][$label] = round($sum, 12); $chartData[2]['entries'][$label] = round($sum, 12);
$start = Navigation::addPeriod($start, $range, 0); $start = app('navigation')->addPeriod($start, $range, 0);
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
@ -180,7 +179,7 @@ class CategoryController extends Controller
} }
$expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end); $expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end);
$income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end); $income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$chartData = [ $chartData = [
[ [
'label' => strval(trans('firefly.spent')), 'label' => strval(trans('firefly.spent')),
@ -236,7 +235,7 @@ class CategoryController extends Controller
} }
$expenses = $repository->periodExpensesNoCategory($accounts, $start, $end); $expenses = $repository->periodExpensesNoCategory($accounts, $start, $end);
$income = $repository->periodIncomeNoCategory($accounts, $start, $end); $income = $repository->periodIncomeNoCategory($accounts, $start, $end);
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$chartData = [ $chartData = [
[ [
'label' => strval(trans('firefly.spent')), 'label' => strval(trans('firefly.spent')),
@ -281,8 +280,8 @@ class CategoryController extends Controller
public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, Carbon $date) public function specificPeriod(CategoryRepositoryInterface $repository, Category $category, Carbon $date)
{ {
$range = Preferences::get('viewRange', '1M')->data; $range = Preferences::get('viewRange', '1M')->data;
$start = Navigation::startOfPeriod($date, $range); $start = app('navigation')->startOfPeriod($date, $range);
$end = Navigation::endOfPeriod($date, $range); $end = app('navigation')->endOfPeriod($date, $range);
$data = $this->makePeriodChart($repository, $category, $start, $end); $data = $this->makePeriodChart($repository, $category, $start, $end);
return Response::json($data); return Response::json($data);
@ -336,7 +335,7 @@ class CategoryController extends Controller
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start); $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start);
$sum = bcadd($spent, $earned); $sum = bcadd($spent, $earned);
$label = trim(Navigation::periodShow($start, '1D')); $label = trim(app('navigation')->periodShow($start, '1D'));
$chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12); $chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12);
$chartData[1]['entries'][$label] = round($earned, 12); $chartData[1]['entries'][$label] = round($earned, 12);

View File

@ -36,7 +36,6 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Response; use Response;
/** /**
@ -177,8 +176,8 @@ class CategoryReportController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$format = Navigation::preferredCarbonLocalizedFormat($start, $end); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end); $function = app('navigation')->preferredEndOfPeriod($start, $end);
$chartData = []; $chartData = [];
$currentStart = clone $start; $currentStart = clone $start;

View File

@ -0,0 +1,266 @@
<?php
/**
* ExpenseReportController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Response;
/**
* Separate controller because many helper functions are shared.
*
* Class ExpenseReportController
*/
class ExpenseReportController extends Controller
{
/** @var AccountRepositoryInterface */
protected $accountRepository;
/** @var GeneratorInterface */
protected $generator;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->generator = app(GeneratorInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request);
}
);
}
/**
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Http\JsonResponse
*/
public function mainChart(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
$cache = new CacheProperties;
$cache->addProperty('chart.expense.report.main');
$cache->addProperty($accounts);
$cache->addProperty($expense);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = app('navigation')->preferredEndOfPeriod($start, $end);
$chartData = [];
$currentStart = clone $start;
$combined = $this->combineAccounts($expense);
// make "all" set:
$all = new Collection;
foreach ($combined as $name => $combi) {
$all = $all->merge($combi);
}
// prep chart data:
foreach ($combined as $name => $combi) {
// first is always expense account:
/** @var Account $exp */
$exp = $combi->first();
$chartData[$exp->id . '-in'] = [
'label' => $name . ' (' . strtolower(strval(trans('firefly.income'))) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
$chartData[$exp->id . '-out'] = [
'label' => $name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')',
'type' => 'bar',
'yAxisID' => 'y-axis-0',
'entries' => [],
];
// total in, total out:
$chartData[$exp->id . '-total-in'] = [
'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
$chartData[$exp->id . '-total-out'] = [
'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')',
'type' => 'line',
'fill' => false,
'yAxisID' => 'y-axis-1',
'entries' => [],
];
}
$sumOfIncome = [];
$sumOfExpense = [];
while ($currentStart < $end) {
$currentEnd = clone $currentStart;
$currentEnd = $currentEnd->$function();
// get expenses grouped by opposing name:
$expenses = $this->groupByName($this->getExpenses($accounts, $all, $currentStart, $currentEnd));
$income = $this->groupByName($this->getIncome($accounts, $all, $currentStart, $currentEnd));
$label = $currentStart->formatLocalized($format);
foreach ($combined as $name => $combi) {
// first is always expense account:
/** @var Account $exp */
$exp = $combi->first();
$labelIn = $exp->id . '-in';
$labelOut = $exp->id . '-out';
$labelSumIn = $exp->id . '-total-in';
$labelSumOut = $exp->id . '-total-out';
$currentIncome = $income[$name] ?? '0';
$currentExpense = $expenses[$name] ?? '0';
// add to sum:
$sumOfIncome[$exp->id] = $sumOfIncome[$exp->id] ?? '0';
$sumOfExpense[$exp->id] = $sumOfExpense[$exp->id] ?? '0';
$sumOfIncome[$exp->id] = bcadd($sumOfIncome[$exp->id], $currentIncome);
$sumOfExpense[$exp->id] = bcadd($sumOfExpense[$exp->id], $currentExpense);
// add to chart:
$chartData[$labelIn]['entries'][$label] = $currentIncome;
$chartData[$labelOut]['entries'][$label] = $currentExpense;
$chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$exp->id];
$chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$exp->id];
}
$currentStart = clone $currentEnd;
$currentStart->addDay();
}
// remove all empty entries to prevent cluttering:
$newSet = [];
foreach ($chartData as $key => $entry) {
if (0 === !array_sum($entry['entries'])) {
$newSet[$key] = $chartData[$key];
}
}
if (0 === count($newSet)) {
$newSet = $chartData; // @codeCoverageIgnore
}
$data = $this->generator->multiSet($newSet);
$cache->store($data);
return Response::json($data);
}
/**
* @param Collection $accounts
*
* @return array
*/
protected function combineAccounts(Collection $accounts): array
{
$combined = [];
/** @var Account $expenseAccount */
foreach ($accounts as $expenseAccount) {
$collection = new Collection;
$collection->push($expenseAccount);
$revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]);
if (!is_null($revenue->id)) {
$collection->push($revenue);
}
$combined[$expenseAccount->name] = $collection;
}
return $combined;
}
/**
* @param Collection $accounts
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getExpenses(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setOpposingAccounts($opposing);
$transactions = $collector->getJournals();
return $transactions;
}
/**
* @param Collection $accounts
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
private function getIncome(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection
{
/** @var JournalCollectorInterface $collector */
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setOpposingAccounts($opposing);
$transactions = $collector->getJournals();
return $transactions;
}
/**
* @param Collection $set
*
* @return array
*/
private function groupByName(Collection $set): array
{
// group by opposing account name.
$grouped = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$name = $transaction->opposing_account_name;
$grouped[$name] = $grouped[$name] ?? '0';
$grouped[$name] = bcadd($transaction->transaction_amount, $grouped[$name]);
}
return $grouped;
}
}

View File

@ -29,7 +29,6 @@ use FireflyIII\Repositories\Account\AccountTaskerInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Navigation;
use Response; use Response;
use Steam; use Steam;
@ -89,7 +88,7 @@ class ReportController extends Controller
} }
/** /**
* Shows income and expense, debet/credit: operations. * Shows income and expense, debit/credit: operations.
* *
* @param Collection $accounts * @param Collection $accounts
* @param Carbon $start * @param Carbon $start
@ -109,7 +108,7 @@ class ReportController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray()); Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());
$format = Navigation::preferredCarbonLocalizedFormat($start, $end); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$source = $this->getChartData($accounts, $start, $end); $source = $this->getChartData($accounts, $start, $end);
$chartData = [ $chartData = [
[ [
@ -144,7 +143,7 @@ class ReportController extends Controller
} }
/** /**
* Shows sum income and expense, debet/credit: operations. * Shows sum income and expense, debit/credit: operations.
* *
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
@ -256,7 +255,7 @@ class ReportController extends Controller
$tasker = app(AccountTaskerInterface::class); $tasker = app(AccountTaskerInterface::class);
while ($currentStart <= $end) { while ($currentStart <= $end) {
$currentEnd = Navigation::endOfPeriod($currentStart, '1M'); $currentEnd = app('navigation')->endOfPeriod($currentStart, '1M');
$earned = strval( $earned = strval(
array_sum( array_sum(
array_map( array_map(
@ -282,7 +281,7 @@ class ReportController extends Controller
$label = $currentStart->format('Y-m') . '-01'; $label = $currentStart->format('Y-m') . '-01';
$spentArray[$label] = bcmul($spent, '-1'); $spentArray[$label] = bcmul($spent, '-1');
$earnedArray[$label] = $earned; $earnedArray[$label] = $earned;
$currentStart = Navigation::addPeriod($currentStart, '1M', 0); $currentStart = app('navigation')->addPeriod($currentStart, '1M', 0);
} }
$result = [ $result = [
'spent' => $spentArray, 'spent' => $spentArray,

View File

@ -36,9 +36,11 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
use Response; use Response;
/**
* Class TagReportController
*/
class TagReportController extends Controller class TagReportController extends Controller
{ {
/** @var GeneratorInterface */ /** @var GeneratorInterface */
@ -168,8 +170,8 @@ class TagReportController extends Controller
return Response::json($cache->get()); // @codeCoverageIgnore return Response::json($cache->get()); // @codeCoverageIgnore
} }
$format = Navigation::preferredCarbonLocalizedFormat($start, $end); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = Navigation::preferredEndOfPeriod($start, $end); $function = app('navigation')->preferredEndOfPeriod($start, $end);
$chartData = []; $chartData = [];
$currentStart = clone $start; $currentStart = clone $start;

View File

@ -126,7 +126,7 @@ class Controller extends BaseController
$uri = $this->redirectUri; $uri = $this->redirectUri;
} }
if (!(false === strpos($uri, 'jscript'))) { if (!(false === strpos($uri, 'jscript'))) {
$uri = $this->redirectUri; $uri = $this->redirectUri; // @codeCoverageIgnore
} }
return $uri; return $uri;

View File

@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Log; use Log;
use Preferences; use Preferences;
use View; use View;
@ -52,8 +53,8 @@ class CurrencyController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.currencies')); app('view')->share('title', trans('firefly.currencies'));
View::share('mainTitleIcon', 'fa-usd'); app('view')->share('mainTitleIcon', 'fa-usd');
$this->repository = app(CurrencyRepositoryInterface::class); $this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class); $this->userRepository = app(UserRepositoryInterface::class);
@ -83,8 +84,6 @@ class CurrencyController extends Controller
$this->rememberPreviousUri('currencies.create.uri'); $this->rememberPreviousUri('currencies.create.uri');
} }
$request->session()->forget('currencies.create.fromStore'); $request->session()->forget('currencies.create.fromStore');
$request->session()->flash('gaEventCategory', 'currency');
$request->session()->flash('gaEventAction', 'create');
return view('currencies.create', compact('subTitleIcon', 'subTitle')); return view('currencies.create', compact('subTitleIcon', 'subTitle'));
} }
@ -131,8 +130,6 @@ class CurrencyController extends Controller
// put previous url in session // put previous url in session
$this->rememberPreviousUri('currencies.delete.uri'); $this->rememberPreviousUri('currencies.delete.uri');
$request->session()->flash('gaEventCategory', 'currency');
$request->session()->flash('gaEventAction', 'delete');
$subTitle = trans('form.delete_currency', ['name' => $currency->name]); $subTitle = trans('form.delete_currency', ['name' => $currency->name]);
return view('currencies.delete', compact('currency', 'subTitle')); return view('currencies.delete', compact('currency', 'subTitle'));
@ -191,8 +188,6 @@ class CurrencyController extends Controller
$this->rememberPreviousUri('currencies.edit.uri'); $this->rememberPreviousUri('currencies.edit.uri');
} }
$request->session()->forget('currencies.edit.fromUpdate'); $request->session()->forget('currencies.edit.fromUpdate');
$request->session()->flash('gaEventCategory', 'currency');
$request->session()->flash('gaEventAction', 'edit');
return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon')); return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon'));
} }
@ -204,12 +199,19 @@ class CurrencyController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$currencies = $this->repository->get(); $page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$currencies = $currencies->sortBy( $pageSize = intval(Preferences::get('listPageSize', 50)->data);
$collection = $this->repository->get();
$total = $collection->count();
$collection = $collection->sortBy(
function (TransactionCurrency $currency) { function (TransactionCurrency $currency) {
return $currency->name; return $currency->name;
} }
); );
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
$currencies = new LengthAwarePaginator($collection, $total, $pageSize, $page);
$currencies->setPath(route('currencies.index'));
$defaultCurrency = $this->repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR'))); $defaultCurrency = $this->repository->getCurrencyByPreference(Preferences::get('currencyPreference', config('firefly.default_currency', 'EUR')));
$isOwner = true; $isOwner = true;
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) { if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {

View File

@ -26,6 +26,7 @@ use Carbon\Carbon;
use ExpandedForm; use ExpandedForm;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Export\ProcessorInterface; use FireflyIII\Export\ProcessorInterface;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Requests\ExportFormRequest; use FireflyIII\Http\Requests\ExportFormRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\ExportJob; use FireflyIII\Models\ExportJob;
@ -50,12 +51,13 @@ class ExportController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('mainTitleIcon', 'fa-file-archive-o'); app('view')->share('mainTitleIcon', 'fa-file-archive-o');
View::share('title', trans('firefly.export_and_backup_data')); app('view')->share('title', trans('firefly.export_and_backup_data'));
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class)->except(['index']);
} }
/** /**

View File

@ -25,8 +25,12 @@ namespace FireflyIII\Http\Controllers;
use Artisan; use Artisan;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use Exception;
use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
@ -36,8 +40,8 @@ use Illuminate\Support\Collection;
use Log; use Log;
use Monolog\Handler\RotatingFileHandler; use Monolog\Handler\RotatingFileHandler;
use Preferences; use Preferences;
use Response;
use Route as RouteFacade; use Route as RouteFacade;
use Session;
use View; use View;
/** /**
@ -51,12 +55,16 @@ class HomeController extends Controller
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
View::share('title', 'Firefly III'); app('view')->share('title', 'Firefly III');
View::share('mainTitleIcon', 'fa-fire'); app('view')->share('mainTitleIcon', 'fa-fire');
$this->middleware(IsDemoUser::class)->except(['dateRange', 'index']);
$this->middleware(IsSandStormUser::class)->only('routes');
} }
/** /**
* @param Request $request * @param Request $request
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function dateRange(Request $request) public function dateRange(Request $request)
{ {
@ -77,12 +85,17 @@ class HomeController extends Controller
$diff = $start->diffInDays($end); $diff = $start->diffInDays($end);
if ($diff > 50) { if ($diff > 50) {
Session::flash('warning', strval(trans('firefly.warning_much_data', ['days' => $diff]))); $request->session()->flash('warning', strval(trans('firefly.warning_much_data', ['days' => $diff])));
} }
Session::put('is_custom_range', $isCustomRange); $request->session()->put('is_custom_range', $isCustomRange);
Session::put('start', $start); Log::debug(sprintf('Set is_custom_range to %s', var_export($isCustomRange, true)));
Session::put('end', $end); $request->session()->put('start', $start);
Log::debug(sprintf('Set start to %s', $start->format('Y-m-d H:i:s')));
$request->session()->put('end', $end);
Log::debug(sprintf('Set end to %s', $end->format('Y-m-d H:i:s')));
return Response::json(['ok' => 'ok']);
} }
/** /**
@ -92,7 +105,7 @@ class HomeController extends Controller
*/ */
public function displayDebug(Request $request) public function displayDebug(Request $request)
{ {
$phpVersion = PHP_VERSION; $phpVersion = str_replace('~', '\~', PHP_VERSION);
$phpOs = php_uname(); $phpOs = php_uname();
$interface = PHP_SAPI; $interface = PHP_SAPI;
$now = Carbon::create()->format('Y-m-d H:i:s e'); $now = Carbon::create()->format('Y-m-d H:i:s e');
@ -164,7 +177,21 @@ class HomeController extends Controller
{ {
Preferences::mark(); Preferences::mark();
$request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range']); $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range']);
Log::debug('Call cache:clear...');
Artisan::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');
} catch (Exception $e) {
// dont care
}
Log::debug('Call view:clear...');
Artisan::call('view:clear');
Log::debug('Done! Redirecting...');
return redirect(route('index')); return redirect(route('index'));
} }
@ -182,7 +209,6 @@ class HomeController extends Controller
if (0 === $count) { if (0 === $count) {
return redirect(route('new-user.index')); return redirect(route('new-user.index'));
} }
$subTitle = trans('firefly.welcomeBack'); $subTitle = trans('firefly.welcomeBack');
$transactions = []; $transactions = [];
$frontPage = Preferences::get( $frontPage = Preferences::get(
@ -209,12 +235,18 @@ class HomeController extends Controller
$transactions[] = [$set, $account]; $transactions[] = [$set, $account];
} }
// fire check update event:
event(new RequestedVersionCheckStatus(auth()->user()));
return view( return view(
'index', 'index',
compact('count', 'subTitle', 'transactions', 'showDeps', 'billCount', 'start', 'end', 'today') compact('count', 'subTitle', 'transactions', 'showDeps', 'billCount', 'start', 'end', 'today')
); );
} }
/**
* @return string
*/
public function routes() public function routes()
{ {
$set = RouteFacade::getRoutes(); $set = RouteFacade::getRoutes();
@ -225,7 +257,7 @@ class HomeController extends Controller
'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch', 'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch',
'two-factor.lost', 'report.options', 'two-factor.lost', 'report.options',
]; ];
$return = '&nbsp;';
/** @var Route $route */ /** @var Route $route */
foreach ($set as $route) { foreach ($set as $route) {
$name = $route->getName(); $name = $route->getName();
@ -237,23 +269,25 @@ class HomeController extends Controller
} }
} }
if (!$found) { if (!$found) {
echo 'touch ' . $route->getName() . '.md;'; $return .= 'touch ' . $route->getName() . '.md;';
} }
} }
} }
return '&nbsp;'; return $return;
} }
/** /**
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function testFlash() public function testFlash(Request $request)
{ {
Session::flash('success', 'This is a success message.'); $request->session()->flash('success', 'This is a success message.');
Session::flash('info', 'This is an info message.'); $request->session()->flash('info', 'This is an info message.');
Session::flash('warning', 'This is a warning.'); $request->session()->flash('warning', 'This is a warning.');
Session::flash('error', 'This is an error!'); $request->session()->flash('error', 'This is an error!');
return redirect(route('home')); return redirect(route('home'));
} }

View File

@ -1,158 +0,0 @@
<?php
/**
* BankController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Support\Import\Information\InformationInterface;
use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface;
use Illuminate\Http\Request;
use Log;
use Session;
class BankController extends Controller
{
/**
* This method must ask the user all parameters necessary to start importing data. This may not be enough
* to finish the import itself (ie. mapping) but it should be enough to begin: accounts to import from,
* accounts to import into, data ranges, etc.
*
* @param string $bank
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*/
public function form(string $bank)
{
$class = config(sprintf('firefly.import_pre.%s', $bank));
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if ($object->hasPrerequisites()) {
return redirect(route('import.bank.prerequisites', [$bank]));
}
$class = config(sprintf('firefly.import_info.%s', $bank));
/** @var InformationInterface $object */
$object = app($class);
$object->setUser(auth()->user());
$remoteAccounts = $object->getAccounts();
return view('import.bank.form', compact('remoteAccounts', 'bank'));
}
/**
* With the information given in the submitted form Firefly III will call upon the bank's classes to return transaction
* information as requested. The user will be able to map unknown data and continue. Or maybe, it's put into some kind of
* fake CSV file and forwarded to the import routine.
*
* @param Request $request
* @param string $bank
*
* @return \Illuminate\Http\RedirectResponse|null
*/
public function postForm(Request $request, string $bank)
{
$class = config(sprintf('firefly.import_pre.%s', $bank));
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if ($object->hasPrerequisites()) {
return redirect(route('import.bank.prerequisites', [$bank]));
}
$remoteAccounts = $request->get('do_import');
if (!is_array($remoteAccounts) || 0 === count($remoteAccounts)) {
Session::flash('error', 'Must select accounts');
return redirect(route('import.bank.form', [$bank]));
}
$remoteAccounts = array_keys($remoteAccounts);
$class = config(sprintf('firefly.import_pre.%s', $bank));
// get import file
unset($remoteAccounts, $class);
// get import config
}
/**
* This method processes the prerequisites the user has entered in the previous step.
*
* Whatever storePrerequisites does, it should make sure that the system is ready to continue immediately. So
* no extra calls or stuff, except maybe to open a session
*
* @see PrerequisitesInterface::storePrerequisites
*
* @param Request $request
* @param string $bank
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postPrerequisites(Request $request, string $bank)
{
Log::debug(sprintf('Now in postPrerequisites for %s', $bank));
$class = config(sprintf('firefly.import_pre.%s', $bank));
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if (!$object->hasPrerequisites()) {
Log::debug(sprintf('No more prerequisites for %s, move to form.', $bank));
return redirect(route('import.bank.form', [$bank]));
}
Log::debug('Going to store entered preprerequisites.');
// store post data
$result = $object->storePrerequisites($request);
if ($result->count() > 0) {
Session::flash('error', $result->first());
return redirect(route('import.bank.prerequisites', [$bank]));
}
return redirect(route('import.bank.form', [$bank]));
}
/**
* This method shows you, if necessary, a form that allows you to enter any required values, such as API keys,
* login passwords or other values.
*
* @param string $bank
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*/
public function prerequisites(string $bank)
{
$class = config(sprintf('firefly.import_pre.%s', $bank));
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if ($object->hasPrerequisites()) {
$view = $object->getView();
$parameters = $object->getViewParameters();
return view($view, $parameters);
}
return redirect(route('import.bank.form', [$bank]));
}
}

View File

@ -0,0 +1,144 @@
<?php
/**
* ConfigurationController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Configuration\ConfiguratorInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
use Log;
/**
* Class ConfigurationController
*/
class ConfigurationController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
}
/**
* Configure the job. This method is returned to until job is deemed "configured".
*
* @param ImportJob $job
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*
* @throws FireflyException
*/
public function index(ImportJob $job)
{
// create configuration class:
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
$this->repository->updateStatus($job, 'configured');
return redirect(route('import.status', [$job->key]));
}
$this->repository->updateStatus($job, 'configuring');
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = trans('firefly.import_config_bread_crumb');
$subTitleIcon = 'fa-wrench';
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
}
/**
* Store the configuration. Returns to "configure" method until job is configured.
*
* @param Request $request
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function post(Request $request, ImportJob $job)
{
Log::debug('Now in postConfigure()', ['job' => $job->key]);
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
return redirect(route('import.status', [$job->key]));
}
$data = $request->all();
$configurator->configureJob($data);
// get possible warning from configurator:
$warning = $configurator->getWarningMessage();
if (strlen($warning) > 0) {
$request->session()->flash('warning', $warning);
}
// return to configure
return redirect(route('import.configure', [$job->key]));
}
/**
* @param ImportJob $job
*
* @return ConfiguratorInterface
*
* @throws FireflyException
*/
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
{
$type = $job->file_type;
$key = sprintf('import.configuration.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var ConfiguratorInterface $configurator */
$configurator = app($className);
$configurator->setJob($job);
return $configurator;
}
}

View File

@ -1,311 +0,0 @@
<?php
/**
* FileController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\ImportUploadRequest;
use FireflyIII\Import\Configurator\ConfiguratorInterface;
use FireflyIII\Import\Routine\ImportRoutine;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Http\Response as LaravelResponse;
use Log;
use Response;
use Session;
use View;
/**
* Class FileController.
*/
class FileController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* This is step 3. This repeats until the job is configured.
*
* @param ImportJob $job
*
* @return View
*
* @throws FireflyException
*/
public function configure(ImportJob $job)
{
// create configuration class:
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
$this->repository->updateStatus($job, 'configured');
return redirect(route('import.file.status', [$job->key]));
}
$view = $configurator->getNextView();
$data = $configurator->getNextData();
$subTitle = trans('firefly.import_config_bread_crumb');
$subTitleIcon = 'fa-wrench';
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
}
/**
* Generate a JSON file of the job's configuration and send it to the user.
*
* @param ImportJob $job
*
* @return LaravelResponse
*/
public function download(ImportJob $job)
{
Log::debug('Now in download()', ['job' => $job->key]);
$config = $job->configuration;
// This is CSV import specific:
$config['column-roles-complete'] = false;
$config['column-mapping-complete'] = false;
$config['initial-config-complete'] = false;
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
$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)
->header('Content-Type', 'application/json')
->header('Content-Description', 'File Transfer')
->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($result));
return $response;
}
/**
* This is step 1. Upload a file.
*
* @return View
*/
public function index()
{
$subTitle = trans('firefly.import_index_sub_title');
$subTitleIcon = 'fa-home';
$importFileTypes = [];
$defaultImportType = config('firefly.default_import_format');
foreach (array_keys(config('firefly.import_formats')) as $type) {
$importFileTypes[$type] = trans('firefly.import_file_type_' . $type);
}
return view('import.file.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType'));
}
/**
* This is step 2. It creates an Import Job. Stores the import.
*
* @param ImportUploadRequest $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function initialize(ImportUploadRequest $request)
{
Log::debug('Now in initialize()');
// create import job:
$type = $request->get('import_file_type');
$job = $this->repository->create($type);
Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]);
// process file:
$this->repository->processFile($job, $request->files->get('import_file'));
// process config, if present:
if ($request->files->has('configuration_file')) {
$this->repository->processConfiguration($job, $request->files->get('configuration_file'));
}
$this->repository->updateStatus($job, 'initialized');
return redirect(route('import.file.configure', [$job->key]));
}
/**
* Show status of import job in JSON.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*/
public function json(ImportJob $job)
{
$result = [
'started' => false,
'finished' => false,
'running' => false,
'errors' => array_values($job->extended_status['errors']),
'percentage' => 0,
'show_percentage' => false,
'steps' => $job->extended_status['steps'],
'done' => $job->extended_status['done'],
'statusText' => trans('firefly.import_status_job_' . $job->status),
'status' => $job->status,
'finishedText' => '',
];
if (0 !== $job->extended_status['steps']) {
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
$result['show_percentage'] = true;
}
if ('finished' === $job->status) {
$tagId = $job->extended_status['tag'];
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tag = $repository->find($tagId);
$result['finished'] = true;
$result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
}
if ('running' === $job->status) {
$result['started'] = true;
$result['running'] = true;
}
return Response::json($result);
}
/**
* Step 4. Save the configuration.
*
* @param Request $request
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postConfigure(Request $request, ImportJob $job)
{
Log::debug('Now in postConfigure()', ['job' => $job->key]);
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
return redirect(route('import.file.status', [$job->key]));
}
$data = $request->all();
$configurator->configureJob($data);
// get possible warning from configurator:
$warning = $configurator->getWarningMessage();
if (strlen($warning) > 0) {
Session::flash('warning', $warning);
}
// return to configure
return redirect(route('import.file.configure', [$job->key]));
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*
* @throws FireflyException
*/
public function start(ImportJob $job)
{
/** @var ImportRoutine $routine */
$routine = app(ImportRoutine::class);
$routine->setJob($job);
$result = $routine->run();
if ($result) {
return Response::json(['run' => 'ok']);
}
throw new FireflyException('Job did not complete succesfully.');
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function status(ImportJob $job)
{
$statuses = ['configured', 'running', 'finished'];
if (!in_array($job->status, $statuses)) {
return redirect(route('import.file.configure', [$job->key]));
}
$subTitle = trans('firefly.import_status_sub_title');
$subTitleIcon = 'fa-star';
return view('import.file.status', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* @param ImportJob $job
*
* @return ConfiguratorInterface
*
* @throws FireflyException
*/
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
{
$type = $job->file_type;
$key = sprintf('firefly.import_configurators.%s', $type);
$className = config($key);
if (null === $className) {
throw new FireflyException('Cannot find configurator class for this job.'); // @codeCoverageIgnore
}
/** @var ConfiguratorInterface $configurator */
$configurator = app($className);
$configurator->setJob($job);
return $configurator;
}
}

View File

@ -0,0 +1,162 @@
<?php
/**
* IndexController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Response as LaravelResponse;
use Log;
use Response;
use View;
/**
* Class FileController.
*/
class IndexController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['create', 'index']);
}
/**
* Creates a new import job for $bank with the default (global) job configuration.
*
* @param string $bank
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function create(string $bank)
{
if (true === !(config(sprintf('import.enabled.%s', $bank)))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
}
$importJob = $this->repository->create($bank);
// from here, always go to configure step.
return redirect(route('import.configure', [$importJob->key]));
}
/**
* Generate a JSON file of the job's configuration and send it to the user.
*
* @param ImportJob $job
*
* @return LaravelResponse
*/
public function download(ImportJob $job)
{
Log::debug('Now in download()', ['job' => $job->key]);
$config = $job->configuration;
// This is CSV import specific:
$config['column-roles-complete'] = false;
$config['column-mapping-complete'] = false;
$config['initial-config-complete'] = false;
$config['has-file-upload'] = false;
$config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
$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)
->header('Content-Type', 'application/json')
->header('Content-Description', 'File Transfer')
->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($result));
return $response;
}
/**
* General import index.
*
* @return View
*/
public function index()
{
$subTitle = trans('firefly.import_index_sub_title');
$subTitleIcon = 'fa-home';
$routines = config('import.enabled');
return view('import.index', compact('subTitle', 'subTitleIcon', 'routines'));
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*
* @throws FireflyException
*/
public function start(ImportJob $job)
{
$type = $job->file_type;
$key = sprintf('import.routine.%s', $type);
$className = config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
}
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job);
$result = $routine->run();
if ($result) {
return Response::json(['run' => 'ok']);
}
throw new FireflyException('Job did not complete successfully. Please review the log files.');
}
}

View File

@ -0,0 +1,138 @@
<?php
/**
* PrerequisitesController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use Illuminate\Http\Request;
use Log;
/**
* Class PrerequisitesController
*/
class PrerequisitesController extends Controller
{
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
return $next($request);
}
);
$this->middleware(IsDemoUser::class);
}
/**
* Once there are no prerequisites, this method will create an importjob object and
* redirect the user to a view where this object can be used by a bank specific
* class to process.
*
* @param string $bank
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
*
* @throws FireflyException
*/
public function index(string $bank)
{
if (true === !(config(sprintf('import.enabled.%s', $bank)))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
}
$class = strval(config(sprintf('import.prerequisites.%s', $bank)));
if (!class_exists($class)) {
throw new FireflyException(sprintf('No class to handle "%s".', $bank)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if ($object->hasPrerequisites()) {
$view = $object->getView();
$parameters = ['title' => strval(trans('firefly.import_index_title')), 'mainTitleIcon' => 'fa-archive'];
$parameters = array_merge($object->getViewParameters(), $parameters);
return view($view, $parameters);
}
// if no (more) prerequisites, return to create a job:
return redirect(route('import.create-job', [$bank]));
}
/**
* This method processes the prerequisites the user has entered in the previous step.
*
* Whatever storePrerequisites does, it should make sure that the system is ready to continue immediately. So
* no extra calls or stuff, except maybe to open a session
*
* @see PrerequisitesInterface::storePrerequisites
*
* @param Request $request
* @param string $bank
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws FireflyException
*/
public function post(Request $request, string $bank)
{
Log::debug(sprintf('Now in postPrerequisites for %s', $bank));
if (true === !(config(sprintf('import.enabled.%s', $bank)))) {
throw new FireflyException(sprintf('Cannot import from "%s" at this time.', $bank)); // @codeCoverageIgnore
}
$class = strval(config(sprintf('import.prerequisites.%s', $bank)));
if (!class_exists($class)) {
throw new FireflyException(sprintf('Cannot find class %s', $class)); // @codeCoverageIgnore
}
/** @var PrerequisitesInterface $object */
$object = app($class);
$object->setUser(auth()->user());
if (!$object->hasPrerequisites()) {
Log::debug(sprintf('No more prerequisites for %s, move to form.', $bank));
return redirect(route('import.create-job', [$bank]));
}
Log::debug('Going to store entered prerequisites.');
// store post data
$result = $object->storePrerequisites($request);
if ($result->count() > 0) {
$request->session()->flash('error', $result->first());
}
return redirect(route('import.prerequisites', [$bank]));
}
}

View File

@ -0,0 +1,113 @@
<?php
/**
* StatusController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Response;
/**
* Class StatusController
*/
class StatusController extends Controller
{
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title'));
return $next($request);
}
);
}
/**
* @param ImportJob $job
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*/
public function index(ImportJob $job)
{
$statuses = ['configured', 'running', 'finished', 'error'];
if (!in_array($job->status, $statuses)) {
return redirect(route('import.configure', [$job->key]));
}
$subTitle = trans('import.status_sub_title');
$subTitleIcon = 'fa-star';
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
}
/**
* Show status of import job in JSON.
*
* @param ImportJob $job
*
* @return \Illuminate\Http\JsonResponse
*/
public function json(ImportJob $job)
{
$result = [
'started' => false,
'finished' => false,
'running' => false,
'errors' => array_values($job->extended_status['errors']),
'percentage' => 0,
'show_percentage' => false,
'steps' => $job->extended_status['steps'],
'done' => $job->extended_status['done'],
'statusText' => trans('import.status_job_' . $job->status),
'status' => $job->status,
'finishedText' => '',
];
if (0 !== $job->extended_status['steps']) {
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
$result['show_percentage'] = true;
}
if ('finished' === $job->status) {
$tagId = $job->extended_status['tag'];
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tag = $repository->find($tagId);
$result['finished'] = true;
$result['finishedText'] = trans('import.status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
}
if ('running' === $job->status) {
$result['started'] = true;
$result['running'] = true;
}
return Response::json($result);
}
}

View File

@ -1,72 +0,0 @@
<?php
/**
* ImportController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use View;
/**
* Class ImportController.
*/
class ImportController extends Controller
{
/** @var ImportJobRepositoryInterface */
public $repository;
/**
*
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
View::share('mainTitleIcon', 'fa-archive');
View::share('title', trans('firefly.import_index_title'));
$this->repository = app(ImportJobRepositoryInterface::class);
return $next($request);
}
);
}
/**
* General import index.
*
* @return View
*/
public function index()
{
$subTitle = trans('firefly.import_index_sub_title');
$subTitleIcon = 'fa-home';
$importFileTypes = [];
$defaultImportType = config('firefly.default_import_format');
foreach (array_keys(config('firefly.import_formats')) as $type) {
$importFileTypes[$type] = trans('firefly.import_file_type_' . $type);
}
return view('import.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType'));
}
}

View File

@ -30,7 +30,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log; use Log;
use Navigation;
use Preferences; use Preferences;
/** /**
@ -89,6 +88,8 @@ class JavascriptController extends Controller
/** /**
* @param Request $request * @param Request $request
* @param AccountRepositoryInterface $repository
* @param CurrencyRepositoryInterface $currencyRepository
* *
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
@ -139,34 +140,43 @@ class JavascriptController extends Controller
$end = session('end'); $end = session('end');
$first = session('first'); $first = session('first');
$title = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat)); $title = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat));
$isCustom = session('is_custom_range'); $isCustom = true === session('is_custom_range', false);
$today = new Carbon;
$ranges = [ $ranges = [
// first range is the current range: // first range is the current range:
$title => [$start, $end], $title => [$start, $end],
]; ];
Log::debug(sprintf('viewRange is %s', $viewRange)); Log::debug(sprintf('viewRange is %s', $viewRange));
Log::debug(sprintf('isCustom is %s', var_export($isCustom, true)));
// when current range is a custom range, add the current period as the next range. // when current range is a custom range, add the current period as the next range.
if ($isCustom) { if ($isCustom) {
Log::debug('Custom is true.'); Log::debug('Custom is true.');
$index = Navigation::periodShow($start, $viewRange); $index = app('navigation')->periodShow($start, $viewRange);
$customPeriodStart = Navigation::startOfPeriod($start, $viewRange); $customPeriodStart = app('navigation')->startOfPeriod($start, $viewRange);
$customPeriodEnd = Navigation::endOfPeriod($customPeriodStart, $viewRange); $customPeriodEnd = app('navigation')->endOfPeriod($customPeriodStart, $viewRange);
$ranges[$index] = [$customPeriodStart, $customPeriodEnd]; $ranges[$index] = [$customPeriodStart, $customPeriodEnd];
} }
// then add previous range and next range // then add previous range and next range
$previousDate = Navigation::subtractPeriod($start, $viewRange); $previousDate = app('navigation')->subtractPeriod($start, $viewRange);
$index = Navigation::periodShow($previousDate, $viewRange); $index = app('navigation')->periodShow($previousDate, $viewRange);
$previousStart = Navigation::startOfPeriod($previousDate, $viewRange); $previousStart = app('navigation')->startOfPeriod($previousDate, $viewRange);
$previousEnd = Navigation::endOfPeriod($previousStart, $viewRange); $previousEnd = app('navigation')->endOfPeriod($previousStart, $viewRange);
$ranges[$index] = [$previousStart, $previousEnd]; $ranges[$index] = [$previousStart, $previousEnd];
$nextDate = Navigation::addPeriod($start, $viewRange, 0); $nextDate = app('navigation')->addPeriod($start, $viewRange, 0);
$index = Navigation::periodShow($nextDate, $viewRange); $index = app('navigation')->periodShow($nextDate, $viewRange);
$nextStart = Navigation::startOfPeriod($nextDate, $viewRange); $nextStart = app('navigation')->startOfPeriod($nextDate, $viewRange);
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); $nextEnd = app('navigation')->endOfPeriod($nextStart, $viewRange);
$ranges[$index] = [$nextStart, $nextEnd]; $ranges[$index] = [$nextStart, $nextEnd];
// today:
$todayStart = app('navigation')->startOfPeriod($today, $viewRange);
$todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange);
if ($todayStart->ne($start) || $todayEnd->ne($end)) {
$ranges[ucfirst(strval(trans('firefly.today')))] = [$todayStart, $todayEnd];
}
// everything // everything
$index = strval(trans('firefly.everything')); $index = strval(trans('firefly.everything'));
$ranges[$index] = [$first, new Carbon]; $ranges[$index] = [$first, new Carbon];

View File

@ -81,7 +81,7 @@ class AutoCompleteController extends Controller
$set = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]); $set = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]);
$filtered = $set->filter( $filtered = $set->filter(
function (Account $account) { function (Account $account) {
if ($account->active) { if ($account->active === true) {
return $account; return $account;
} }
@ -138,7 +138,7 @@ class AutoCompleteController extends Controller
$set = $repository->getAccountsByType([AccountType::REVENUE]); $set = $repository->getAccountsByType([AccountType::REVENUE]);
$filtered = $set->filter( $filtered = $set->filter(
function (Account $account) { function (Account $account) {
if ($account->active) { if ($account->active === true) {
return $account; return $account;
} }

View File

@ -36,6 +36,8 @@ class FrontpageController extends Controller
* @param PiggyBankRepositoryInterface $repository * @param PiggyBankRepositoryInterface $repository
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/ */
public function piggyBanks(PiggyBankRepositoryInterface $repository) public function piggyBanks(PiggyBankRepositoryInterface $repository)
{ {

View File

@ -32,6 +32,8 @@ use Response;
class IntroController class IntroController
{ {
/** /**
* Get the intro steps. There are currently no specific routes with an outro step.
*
* @param string $route * @param string $route
* @param string $specificPage * @param string $specificPage
* *
@ -39,12 +41,16 @@ class IntroController
*/ */
public function getIntroSteps(string $route, string $specificPage = '') public function getIntroSteps(string $route, string $specificPage = '')
{ {
Log::debug(sprintf('getIntroSteps for route "%s" and page "%s"', $route, $specificPage));
$steps = $this->getBasicSteps($route); $steps = $this->getBasicSteps($route);
$specificSteps = $this->getSpecificSteps($route, $specificPage); $specificSteps = $this->getSpecificSteps($route, $specificPage);
if (0 === count($specificSteps)) { if (0 === count($specificSteps)) {
Log::debug(sprintf('No specific steps for route "%s" and page "%s"', $route, $specificPage));
return Response::json($steps); return Response::json($steps);
} }
if ($this->hasOutroStep($route)) { if ($this->hasOutroStep($route)) {
// @codeCoverageIgnoreStart
// save last step: // save last step:
$lastStep = $steps[count($steps) - 1]; $lastStep = $steps[count($steps) - 1];
// remove last step: // remove last step:
@ -52,6 +58,7 @@ class IntroController
// merge arrays and add last step again // merge arrays and add last step again
$steps = array_merge($steps, $specificSteps); $steps = array_merge($steps, $specificSteps);
$steps[] = $lastStep; $steps[] = $lastStep;
// @codeCoverageIgnoreEnd
} }
if (!$this->hasOutroStep($route)) { if (!$this->hasOutroStep($route)) {
$steps = array_merge($steps, $specificSteps); $steps = array_merge($steps, $specificSteps);
@ -68,13 +75,16 @@ class IntroController
public function hasOutroStep(string $route): bool public function hasOutroStep(string $route): bool
{ {
$routeKey = str_replace('.', '_', $route); $routeKey = str_replace('.', '_', $route);
Log::debug(sprintf('Has outro step for route %s', $routeKey));
$elements = config(sprintf('intro.%s', $routeKey)); $elements = config(sprintf('intro.%s', $routeKey));
if (!is_array($elements)) { if (!is_array($elements)) {
return false; return false;
} }
$keys = array_keys($elements); Log::debug('Elements is array', $elements);
Log::debug('Keys is', array_keys($elements));
Log::debug(sprintf('Keys has "outro": %s', var_export(in_array('outro', array_keys($elements)), true)));
return in_array('outro', $keys); return in_array('outro', array_keys($elements));
} }
/** /**
@ -135,6 +145,7 @@ class IntroController
$steps[] = $currentStep; $steps[] = $currentStep;
} }
} }
Log::debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps)));
return $steps; return $steps;
} }
@ -148,6 +159,7 @@ class IntroController
private function getSpecificSteps(string $route, string $specificPage): array private function getSpecificSteps(string $route, string $specificPage): array
{ {
$steps = []; $steps = [];
$routeKey = '';
// user is on page with specific instructions: // user is on page with specific instructions:
if (strlen($specificPage) > 0) { if (strlen($specificPage) > 0) {
@ -165,6 +177,7 @@ class IntroController
} }
} }
} }
Log::debug(sprintf('Total specific steps for route "%s" and page "%s" (routeKey is "%s") is %d', $route, $specificPage, $routeKey, count($steps)));
return $steps; return $steps;
} }

View File

@ -46,6 +46,8 @@ class JsonController extends Controller
* @param Request $request * @param Request $request
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/ */
public function action(Request $request) public function action(Request $request)
{ {
@ -120,6 +122,8 @@ class JsonController extends Controller
* @param Request $request * @param Request $request
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/ */
public function trigger(Request $request) public function trigger(Request $request)
{ {

View File

@ -56,8 +56,8 @@ class NewUserController extends Controller
*/ */
public function index(AccountRepositoryInterface $repository) public function index(AccountRepositoryInterface $repository)
{ {
View::share('title', trans('firefly.welcome')); app('view')->share('title', trans('firefly.welcome'));
View::share('mainTitleIcon', 'fa-fire'); app('view')->share('mainTitleIcon', 'fa-fire');
$types = config('firefly.accountTypesByIdentifier.asset'); $types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types); $count = $repository->count($types);
@ -114,7 +114,7 @@ class NewUserController extends Controller
'virtualBalance' => 0, 'virtualBalance' => 0,
'active' => true, 'active' => true,
'accountRole' => 'defaultAsset', 'accountRole' => 'defaultAsset',
'openingBalance' => round($request->input('bank_balance'), 12), 'openingBalance' => $request->input('bank_balance'),
'openingBalanceDate' => new Carbon, 'openingBalanceDate' => new Carbon,
'currency_id' => intval($request->input('amount_currency_id_bank_balance')), 'currency_id' => intval($request->input('amount_currency_id_bank_balance')),
]; ];
@ -139,7 +139,7 @@ class NewUserController extends Controller
'virtualBalance' => 0, 'virtualBalance' => 0,
'active' => true, 'active' => true,
'accountRole' => 'savingAsset', 'accountRole' => 'savingAsset',
'openingBalance' => round($request->input('savings_balance'), 12), 'openingBalance' => $request->input('savings_balance'),
'openingBalanceDate' => new Carbon, 'openingBalanceDate' => new Carbon,
'currency_id' => intval($request->input('amount_currency_id_bank_balance')), 'currency_id' => intval($request->input('amount_currency_id_bank_balance')),
]; ];

View File

@ -30,7 +30,7 @@ use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Pagination\LengthAwarePaginator;
use Log; use Log;
use Preferences; use Preferences;
use Response; use Response;
@ -52,8 +52,8 @@ class PiggyBankController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.piggyBanks')); app('view')->share('title', trans('firefly.piggyBanks'));
View::share('mainTitleIcon', 'fa-sort-amount-asc'); app('view')->share('mainTitleIcon', 'fa-sort-amount-asc');
return $next($request); return $next($request);
} }
@ -120,8 +120,6 @@ class PiggyBankController extends Controller
$this->rememberPreviousUri('piggy-banks.create.uri'); $this->rememberPreviousUri('piggy-banks.create.uri');
} }
Session::forget('piggy-banks.create.fromStore'); Session::forget('piggy-banks.create.fromStore');
Session::flash('gaEventCategory', 'piggy-banks');
Session::flash('gaEventAction', 'create');
return view('piggy-banks.create', compact('accounts', 'subTitle', 'subTitleIcon')); return view('piggy-banks.create', compact('accounts', 'subTitle', 'subTitleIcon'));
} }
@ -137,8 +135,6 @@ class PiggyBankController extends Controller
// put previous url in session // put previous url in session
$this->rememberPreviousUri('piggy-banks.delete.uri'); $this->rememberPreviousUri('piggy-banks.delete.uri');
Session::flash('gaEventCategory', 'piggy-banks');
Session::flash('gaEventAction', 'delete');
return view('piggy-banks.delete', compact('piggyBank', 'subTitle')); return view('piggy-banks.delete', compact('piggyBank', 'subTitle'));
} }
@ -188,8 +184,6 @@ class PiggyBankController extends Controller
'note' => null === $note ? '' : $note->text, 'note' => null === $note ? '' : $note->text,
]; ];
Session::flash('preFilled', $preFilled); Session::flash('preFilled', $preFilled);
Session::flash('gaEventCategory', 'piggy-banks');
Session::flash('gaEventAction', 'edit');
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
if (true !== session('piggy-banks.edit.fromUpdate')) { if (true !== session('piggy-banks.edit.fromUpdate')) {
@ -205,17 +199,19 @@ class PiggyBankController extends Controller
* *
* @return View * @return View
*/ */
public function index(PiggyBankRepositoryInterface $piggyRepository) public function index(Request $request, PiggyBankRepositoryInterface $piggyRepository)
{ {
/** @var Collection $piggyBanks */ $collection = $piggyRepository->getPiggyBanks();
$piggyBanks = $piggyRepository->getPiggyBanks(); $total = $collection->count();
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end', Carbon::now()->endOfMonth()); $end = session('end', Carbon::now()->endOfMonth());
$accounts = []; $accounts = [];
Log::debug('Looping piggues'); Log::debug('Looping piggues');
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) { foreach ($collection as $piggyBank) {
$piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0'; $piggyBank->savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
$piggyBank->percentage = 0 !== bccomp('0', $piggyBank->savedSoFar) ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0; $piggyBank->percentage = 0 !== bccomp('0', $piggyBank->savedSoFar) ? intval($piggyBank->savedSoFar / $piggyBank->targetamount * 100) : 0;
$piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar)); $piggyBank->leftToSave = bcsub($piggyBank->targetamount, strval($piggyBank->savedSoFar));
@ -242,6 +238,11 @@ class PiggyBankController extends Controller
} }
} }
// paginate piggy banks
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
$piggyBanks = new LengthAwarePaginator($collection, $total, $pageSize, $page);
$piggyBanks->setPath(route('piggy-banks.index'));
return view('piggy-banks.index', compact('piggyBanks', 'accounts')); return view('piggy-banks.index', compact('piggyBanks', 'accounts'));
} }
@ -401,7 +402,6 @@ class PiggyBankController extends Controller
} }
$piggyBank = $repository->store($data); $piggyBank = $repository->store($data);
Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => $piggyBank->name]))); Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => $piggyBank->name])));
Preferences::mark(); Preferences::mark();

View File

@ -32,6 +32,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\Binder\AccountList; use FireflyIII\Support\Binder\AccountList;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use InvalidArgumentException; use InvalidArgumentException;
use Response; use Response;
use View; use View;
@ -81,6 +82,11 @@ class ReportController extends Controller
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
* *
* @throws FireflyException * @throws FireflyException
* @throws \Throwable
* @throws \Throwable
* @throws \Throwable
* @throws \Throwable
* @throws \Throwable
*/ */
public function general(Request $request) public function general(Request $request)
{ {
@ -119,6 +125,7 @@ class ReportController extends Controller
* @return string * @return string
* *
* @throws FireflyException * @throws FireflyException
* @throws \Throwable
*/ */
private function balanceAmount(array $attributes): string private function balanceAmount(array $attributes): string
{ {
@ -156,7 +163,7 @@ class ReportController extends Controller
* *
* @return string * @return string
* *
* @throws FireflyException * @throws \Throwable
*/ */
private function budgetSpentAmount(array $attributes): string private function budgetSpentAmount(array $attributes): string
{ {
@ -170,11 +177,11 @@ class ReportController extends Controller
/** /**
* Returns all expenses in category in range. * Returns all expenses in category in range.
* *
* @param $attributes * @param array $attributes
* *
* @return string * @return string
* *
* @throws FireflyException * @throws \Throwable
*/ */
private function categoryEntry(array $attributes): string private function categoryEntry(array $attributes): string
{ {
@ -188,11 +195,11 @@ class ReportController extends Controller
/** /**
* Returns all the expenses that went to the given expense account. * Returns all the expenses that went to the given expense account.
* *
* @param $attributes * @param array $attributes
* *
* @return string * @return string
* *
* @throws FireflyException * @throws \Throwable
*/ */
private function expenseEntry(array $attributes): string private function expenseEntry(array $attributes): string
{ {
@ -206,11 +213,11 @@ class ReportController extends Controller
/** /**
* Returns all the incomes that went to the given asset account. * Returns all the incomes that went to the given asset account.
* *
* @param $attributes * @param array $attributes
* *
* @return string * @return string
* *
* @throws FireflyException * @throws \Throwable
*/ */
private function incomeEntry(array $attributes): string private function incomeEntry(array $attributes): string
{ {
@ -231,7 +238,7 @@ class ReportController extends Controller
private function parseAttributes(array $attributes): array private function parseAttributes(array $attributes): array
{ {
$attributes['location'] = $attributes['location'] ?? ''; $attributes['location'] = $attributes['location'] ?? '';
$attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', ''); $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', []));
try { try {
$attributes['startDate'] = Carbon::createFromFormat('Ymd', $attributes['startDate']); $attributes['startDate'] = Carbon::createFromFormat('Ymd', $attributes['startDate']);
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {

View File

@ -26,8 +26,8 @@ use FireflyIII\Http\Requests\TokenFormRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Google2FA;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use PragmaRX\Google2FA\Contracts\Google2FA;
use Preferences; use Preferences;
use Session; use Session;
use View; use View;
@ -46,8 +46,8 @@ class PreferencesController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.preferences')); app('view')->share('title', trans('firefly.preferences'));
View::share('mainTitleIcon', 'fa-gear'); app('view')->share('mainTitleIcon', 'fa-gear');
return $next($request); return $next($request);
} }
@ -55,22 +55,23 @@ class PreferencesController extends Controller
} }
/** /**
* @param Google2FA $google2fa
*
* @return View * @return View
*/ */
public function code(Google2FA $google2fa) public function code()
{ {
$domain = $this->getDomain(); $domain = $this->getDomain();
$secret = $google2fa->generateSecretKey(); $secret = Google2FA::generateSecretKey();
Session::flash('two-factor-secret', $secret); Session::flash('two-factor-secret', $secret);
$image = $google2fa->getQRCodeInline($domain, auth()->user()->email, $secret, 200); $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret, 200);
return view('preferences.code', compact('image')); return view('preferences.code', compact('image'));
} }
/** /**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws \Exception
* @throws \Exception
*/ */
public function deleteCode() public function deleteCode()
{ {
@ -94,7 +95,7 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data; $viewRange = $viewRangePref->data;
$frontPageAccounts = Preferences::get('frontPageAccounts', []); $frontPageAccounts = Preferences::get('frontPageAccounts', []);
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
$transactionPageSize = Preferences::get('transactionPageSize', 50)->data; $listPageSize = Preferences::get('listPageSize', 50)->data;
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data; $customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
$showDeps = Preferences::get('showDepositsFrontpage', false)->data; $showDeps = Preferences::get('showDepositsFrontpage', false)->data;
$fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data; $fiscalYearStartStr = Preferences::get('fiscalYearStart', '01-01')->data;
@ -112,7 +113,7 @@ class PreferencesController extends Controller
'tjOptionalFields', 'tjOptionalFields',
'viewRange', 'viewRange',
'customFiscalYear', 'customFiscalYear',
'transactionPageSize', 'listPageSize',
'fiscalYearStart', 'fiscalYearStart',
'is2faEnabled', 'is2faEnabled',
'has2faSecret', 'has2faSecret',
@ -173,10 +174,10 @@ class PreferencesController extends Controller
Preferences::set('showDepositsFrontpage', $showDepositsFrontpage); Preferences::set('showDepositsFrontpage', $showDepositsFrontpage);
// save page size: // save page size:
Preferences::set('transactionPageSize', 50); Preferences::set('listPageSize', 50);
$transactionPageSize = intval($request->get('transactionPageSize')); $listPageSize = intval($request->get('listPageSize'));
if ($transactionPageSize > 0 && $transactionPageSize < 1337) { if ($listPageSize > 0 && $listPageSize < 1337) {
Preferences::set('transactionPageSize', $transactionPageSize); Preferences::set('listPageSize', $listPageSize);
} }
$twoFactorAuthEnabled = false; $twoFactorAuthEnabled = false;

View File

@ -26,7 +26,8 @@ use Auth;
use FireflyIII\Events\UserChangedEmail; use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Exceptions\ValidationException; use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Http\Middleware\IsSandStormUser;
use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\EmailFormRequest; use FireflyIII\Http\Requests\EmailFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest;
@ -56,13 +57,14 @@ class ProfileController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.profile')); app('view')->share('title', trans('firefly.profile'));
View::share('mainTitleIcon', 'fa-user'); app('view')->share('mainTitleIcon', 'fa-user');
return $next($request); return $next($request);
} }
); );
$this->middleware(IsLimitedUser::class)->except(['confirmEmailChange', 'undoEmailChange']); $this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsSandStormUser::class)->except('index');
} }
/** /**
@ -91,30 +93,33 @@ class ProfileController extends Controller
} }
/** /**
* @param UserRepositoryInterface $repository
* @param string $token * @param string $token
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* *
* @throws FireflyException * @throws FireflyException
*/ */
public function confirmEmailChange(string $token) public function confirmEmailChange(UserRepositoryInterface $repository, string $token)
{ {
// find preference with this token value. // find preference with this token value.
$set = Preferences::findByName('email_change_confirm_token'); $set = Preferences::findByName('email_change_confirm_token');
$user = null; $user = null;
Log::debug(sprintf('Found %d preferences', $set->count()));
/** @var Preference $preference */ /** @var Preference $preference */
foreach ($set as $preference) { foreach ($set as $preference) {
if ($preference->data === $token) { if ($preference->data === $token) {
Log::debug('Found user');
$user = $preference->user; $user = $preference->user;
} }
} }
// update user to clear blocked and blocked_code. // update user to clear blocked and blocked_code.
if (null === $user) { if (null === $user) {
Log::debug('Found no user');
throw new FireflyException('Invalid token.'); throw new FireflyException('Invalid token.');
} }
$user->blocked = 0; Log::debug('Will unblock user.');
$user->blocked_code = ''; $repository->unblockUser($user);
$user->save();
// return to login. // return to login.
Session::flash('success', strval(trans('firefly.login_with_new_email'))); Session::flash('success', strval(trans('firefly.login_with_new_email')));
@ -172,7 +177,7 @@ class ProfileController extends Controller
$existing = $repository->findByEmail($newEmail); $existing = $repository->findByEmail($newEmail);
if (null !== $existing) { if (null !== $existing) {
// force user logout. // force user logout.
$this->guard()->logout(); Auth::guard()->logout();
$request->session()->invalidate(); $request->session()->invalidate();
Session::flash('success', strval(trans('firefly.email_changed'))); Session::flash('success', strval(trans('firefly.email_changed')));
@ -241,14 +246,11 @@ class ProfileController extends Controller
Session::flush(); Session::flush();
$repository->destroy($user); $repository->destroy($user);
Session::flash('gaEventCategory', 'user');
Session::flash('gaEventAction', 'delete-account');
return redirect(route('index')); return redirect(route('index'));
} }
/** /**
* * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */
public function regenerate() public function regenerate()
{ {
@ -260,6 +262,7 @@ class ProfileController extends Controller
} }
/** /**
* @param UserRepositoryInterface $repository
* @param string $token * @param string $token
* @param string $hash * @param string $hash
* *
@ -267,7 +270,7 @@ class ProfileController extends Controller
* *
* @throws FireflyException * @throws FireflyException
*/ */
public function undoEmailChange(string $token, string $hash) public function undoEmailChange(UserRepositoryInterface $repository, string $token, string $hash)
{ {
// find preference with this token value. // find preference with this token value.
$set = Preferences::findByName('email_change_undo_token'); $set = Preferences::findByName('email_change_undo_token');
@ -285,6 +288,7 @@ class ProfileController extends Controller
// found user. // found user.
// which email address to return to? // which email address to return to?
$set = Preferences::beginsWith($user, 'previous_email_'); $set = Preferences::beginsWith($user, 'previous_email_');
/** @var string $match */
$match = null; $match = null;
foreach ($set as $entry) { foreach ($set as $entry) {
$hashed = hash('sha256', $entry->data); $hashed = hash('sha256', $entry->data);
@ -297,10 +301,9 @@ class ProfileController extends Controller
throw new FireflyException('Invalid token.'); throw new FireflyException('Invalid token.');
} }
// change user back // change user back
$user->email = $match; // now actually update user:
$user->blocked = 0; $repository->changeEmail($user, $match);
$user->blocked_code = ''; $repository->unblockUser($user);
$user->save();
// return to login. // return to login.
Session::flash('success', strval(trans('firefly.login_with_old_email'))); Session::flash('success', strval(trans('firefly.login_with_old_email')));

View File

@ -39,6 +39,8 @@ class AccountController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function general(Collection $accounts, Carbon $start, Carbon $end) public function general(Collection $accounts, Carbon $start, Carbon $end)
{ {

View File

@ -40,6 +40,8 @@ class BalanceController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function general(BalanceReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end) public function general(BalanceReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end)
{ {

View File

@ -28,7 +28,6 @@ use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
/** /**
* Class BudgetController. * Class BudgetController.
@ -42,6 +41,8 @@ class BudgetController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function general(BudgetReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end) public function general(BudgetReportHelperInterface $helper, Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -69,6 +70,8 @@ class BudgetController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function period(Collection $accounts, Carbon $start, Carbon $end) public function period(Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -88,7 +91,7 @@ class BudgetController extends Controller
$data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end); $data = $repository->getBudgetPeriodReport($budgets, $accounts, $start, $end);
$data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget" $data[0] = $repository->getNoBudgetPeriodReport($accounts, $start, $end); // append report data for "no budget"
$report = $this->filterBudgetPeriodReport($data); $report = $this->filterBudgetPeriodReport($data);
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); $result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
$cache->store($result); $cache->store($result);

View File

@ -28,7 +28,6 @@ use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation;
/** /**
* Class CategoryController. * Class CategoryController.
@ -41,6 +40,8 @@ class CategoryController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function expenses(Collection $accounts, Carbon $start, Carbon $end) public function expenses(Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -58,7 +59,7 @@ class CategoryController extends Controller
$data = $repository->periodExpenses($categories, $accounts, $start, $end); $data = $repository->periodExpenses($categories, $accounts, $start, $end);
$data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end); $data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end);
$report = $this->filterReport($data); $report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render(); $result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result); $cache->store($result);
@ -72,6 +73,8 @@ class CategoryController extends Controller
* @param Collection $accounts * @param Collection $accounts
* *
* @return string * @return string
*
* @throws \Throwable
*/ */
public function income(Collection $accounts, Carbon $start, Carbon $end) public function income(Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -89,7 +92,7 @@ class CategoryController extends Controller
$data = $repository->periodIncome($categories, $accounts, $start, $end); $data = $repository->periodIncome($categories, $accounts, $start, $end);
$data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end); $data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end);
$report = $this->filterReport($data); $report = $this->filterReport($data);
$periods = Navigation::listOfPeriods($start, $end); $periods = app('navigation')->listOfPeriods($start, $end);
$result = view('reports.partials.category-period', compact('report', 'periods'))->render(); $result = view('reports.partials.category-period', compact('report', 'periods'))->render();
$cache->store($result); $cache->store($result);
@ -105,6 +108,8 @@ class CategoryController extends Controller
* @return mixed|string * @return mixed|string
* *
* @internal param ReportHelperInterface $helper * @internal param ReportHelperInterface $helper
*
* @throws \Throwable
*/ */
public function operations(Collection $accounts, Carbon $start, Carbon $end) public function operations(Collection $accounts, Carbon $start, Carbon $end)
{ {

View File

@ -0,0 +1,591 @@
<?php
/**
* ExpenseController.php
* 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 <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
/**
* Class ExpenseController
*/
class ExpenseController extends Controller
{
/** @var AccountRepositoryInterface */
protected $accountRepository;
/**
* Constructor for ExpenseController
*/
public function __construct()
{
parent::__construct();
// translations:
$this->middleware(
function ($request, $next) {
$this->accountRepository = app(AccountRepositoryInterface::class);
return $next($request);
}
);
}
/**
* Generates the overview per budget.
*
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return string
*
* @throws \Throwable
*/
public function budget(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
// Properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-budget');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
foreach ($combined as $name => $combi) {
$all = $all->merge($combi);
}
// now find spent / earned:
$spent = $this->spentByBudget($accounts, $all, $start, $end);
// join arrays somehow:
$together = [];
foreach ($spent as $categoryId => $spentInfo) {
if (!isset($together[$categoryId])) {
$together[$categoryId]['spent'] = $spentInfo;
$together[$categoryId]['budget'] = $spentInfo['name'];
$together[$categoryId]['grand_total'] = '0';
}
$together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']);
}
unset($spentInfo);
$result = view('reports.partials.exp-budgets', compact('together'))->render();
$cache->store($result);
return $result;
}
/**
* Generates the overview per category (spent and earned).
*
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return string
*
* @throws \Throwable
*/
public function category(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
// Properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-category');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
foreach ($combined as $name => $combi) {
$all = $all->merge($combi);
}
// now find spent / earned:
$spent = $this->spentByCategory($accounts, $all, $start, $end);
$earned = $this->earnedByCategory($accounts, $all, $start, $end);
// join arrays somehow:
$together = [];
foreach ($spent as $categoryId => $spentInfo) {
if (!isset($together[$categoryId])) {
$together[$categoryId]['spent'] = $spentInfo;
$together[$categoryId]['category'] = $spentInfo['name'];
$together[$categoryId]['grand_total'] = '0';
}
$together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']);
}
unset($spentInfo);
foreach ($earned as $categoryId => $earnedInfo) {
if (!isset($together[$categoryId])) {
$together[$categoryId]['earned'] = $earnedInfo;
$together[$categoryId]['category'] = $earnedInfo['name'];
$together[$categoryId]['grand_total'] = '0';
}
$together[$categoryId]['grand_total'] = bcadd($earnedInfo['grand_total'], $together[$categoryId]['grand_total']);
}
$result = view('reports.partials.exp-categories', compact('together'))->render();
$cache->store($result);
return $result;
}
/**
* Overview of spending
*
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return array|mixed|string
*
* @throws \Throwable
*/
public function spent(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
// chart properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-spent');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$result = [];
foreach ($combined as $name => $combi) {
/**
* @var string
* @var Collection $combi
*/
$spent = $this->spentInPeriod($accounts, $combi, $start, $end);
$earned = $this->earnedInPeriod($accounts, $combi, $start, $end);
$result[$name] = [
'spent' => $spent,
'earned' => $earned,
];
}
$result = view('reports.partials.exp-not-grouped', compact('result'))->render();
$cache->store($result);
return $result;
// for period, get spent and earned for each account (by name)
}
/**
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return string
*
* @throws \Throwable
*/
public function topExpense(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
// Properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-budget');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
foreach ($combined as $name => $combi) {
$all = $all->merge($combi);
}
// get all expenses in period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts);
$collector->setOpposingAccounts($all);
$set = $collector->getJournals();
$sorted = $set->sortBy(
function (Transaction $transaction) {
return floatval($transaction->transaction_amount);
}
);
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
* @param Collection $expense
* @param Carbon $start
* @param Carbon $end
*
* @return mixed|string
*
* @throws \Throwable
*/
public function topIncome(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
// Properties for cache:
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-budget');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
foreach ($combined as $name => $combi) {
$all = $all->merge($combi);
}
// get all expenses in period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts);
$collector->setOpposingAccounts($all);
$set = $collector->getJournals();
$sorted = $set->sortByDesc(
function (Transaction $transaction) {
return floatval($transaction->transaction_amount);
}
);
$result = view('reports.partials.top-transactions', compact('sorted'))->render();
$cache->store($result);
return $result;
}
/**
* @param Collection $accounts
*
* @return array
*/
protected function combineAccounts(Collection $accounts): array
{
$combined = [];
/** @var Account $expenseAccount */
foreach ($accounts as $expenseAccount) {
$collection = new Collection;
$collection->push($expenseAccount);
$revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]);
if (!is_null($revenue->id)) {
$collection->push($revenue);
}
$combined[$expenseAccount->name] = $collection;
}
return $combined;
}
/**
* @param Collection $assets
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
$set = $collector->getJournals();
$sum = [];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
$categoryName = $transaction->transaction_category_name;
$categoryId = intval($transaction->transaction_category_id);
// if null, grab from journal:
if (0 === $categoryId) {
$categoryName = $transaction->transaction_journal_category_name;
$categoryId = intval($transaction->transaction_journal_category_id);
}
if (0 !== $categoryId) {
$categoryName = app('steam')->tryDecrypt($categoryName);
}
// if not set, set to zero:
if (!isset($sum[$categoryId][$currencyId])) {
$sum[$categoryId] = [
'grand_total' => '0',
'name' => $categoryName,
'per_currency' => [
$currencyId => [
'sum' => '0',
'category' => [
'id' => $categoryId,
'name' => $categoryName,
],
'currency' => [
'symbol' => $transaction->transaction_currency_symbol,
'dp' => $transaction->transaction_currency_dp,
],
],
],
];
}
// add amount
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
);
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
}
return $sum;
}
/**
* @param Collection $assets
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets);
$collector->setOpposingAccounts($opposing);
$set = $collector->getJournals();
$sum = [
'grand_sum' => '0',
'per_currency' => [],
];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
// if not set, set to zero:
if (!isset($sum['per_currency'][$currencyId])) {
$sum['per_currency'][$currencyId] = [
'sum' => '0',
'currency' => [
'symbol' => $transaction->transaction_currency_symbol,
'dp' => $transaction->transaction_currency_dp,
],
];
}
// add amount
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
}
return $sum;
}
/**
* @param Collection $assets
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
$collector->setOpposingAccounts($opposing)->withBudgetInformation();
$set = $collector->getJournals();
$sum = [];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
$budgetName = $transaction->transaction_budget_name;
$budgetId = intval($transaction->transaction_budget_id);
// if null, grab from journal:
if (0 === $budgetId) {
$budgetName = $transaction->transaction_journal_budget_name;
$budgetId = intval($transaction->transaction_journal_budget_id);
}
if (0 !== $budgetId) {
$budgetName = app('steam')->tryDecrypt($budgetName);
}
// if not set, set to zero:
if (!isset($sum[$budgetId][$currencyId])) {
$sum[$budgetId] = [
'grand_total' => '0',
'name' => $budgetName,
'per_currency' => [
$currencyId => [
'sum' => '0',
'budget' => [
'id' => $budgetId,
'name' => $budgetName,
],
'currency' => [
'symbol' => $transaction->transaction_currency_symbol,
'dp' => $transaction->transaction_currency_dp,
],
],
],
];
}
// add amount
$sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd(
$sum[$budgetId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
);
$sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $transaction->transaction_amount);
}
return $sum;
}
/**
* @param Collection $assets
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
$collector->setOpposingAccounts($opposing)->withCategoryInformation();
$set = $collector->getJournals();
$sum = [];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
$categoryName = $transaction->transaction_category_name;
$categoryId = intval($transaction->transaction_category_id);
// if null, grab from journal:
if (0 === $categoryId) {
$categoryName = $transaction->transaction_journal_category_name;
$categoryId = intval($transaction->transaction_journal_category_id);
}
if (0 !== $categoryId) {
$categoryName = app('steam')->tryDecrypt($categoryName);
}
// if not set, set to zero:
if (!isset($sum[$categoryId][$currencyId])) {
$sum[$categoryId] = [
'grand_total' => '0',
'name' => $categoryName,
'per_currency' => [
$currencyId => [
'sum' => '0',
'category' => [
'id' => $categoryId,
'name' => $categoryName,
],
'currency' => [
'symbol' => $transaction->transaction_currency_symbol,
'dp' => $transaction->transaction_currency_dp,
],
],
],
];
}
// add amount
$sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd(
$sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount
);
$sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount);
}
return $sum;
}
/**
* @param Collection $assets
* @param Collection $opposing
* @param Carbon $start
* @param Carbon $end
*
* @return array
*/
protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets);
$collector->setOpposingAccounts($opposing);
$set = $collector->getJournals();
$sum = [
'grand_sum' => '0',
'per_currency' => [],
];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
// if not set, set to zero:
if (!isset($sum['per_currency'][$currencyId])) {
$sum['per_currency'][$currencyId] = [
'sum' => '0',
'currency' => [
'symbol' => $transaction->transaction_currency_symbol,
'dp' => $transaction->transaction_currency_dp,
],
];
}
// add amount
$sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount);
$sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount);
}
return $sum;
}
}

View File

@ -40,6 +40,8 @@ class OperationsController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function expenses(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end) public function expenses(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -67,6 +69,8 @@ class OperationsController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \Throwable
*/ */
public function income(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end) public function income(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -95,6 +99,8 @@ class OperationsController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return mixed|string * @return mixed|string
*
* @throws \Throwable
*/ */
public function operations(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end) public function operations(AccountTaskerInterface $tasker, Collection $accounts, Carbon $start, Carbon $end)
{ {

View File

@ -59,8 +59,8 @@ class ReportController extends Controller
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
View::share('title', trans('firefly.reports')); app('view')->share('title', trans('firefly.reports'));
View::share('mainTitleIcon', 'fa-line-chart'); app('view')->share('mainTitleIcon', 'fa-line-chart');
View::share('subTitleIcon', 'fa-calendar'); View::share('subTitleIcon', 'fa-calendar');
return $next($request); return $next($request);
@ -70,10 +70,47 @@ class ReportController extends Controller
/** /**
* @param Collection $accounts * @param Collection $accounts
* @param Collection $expense
* @param Carbon $start * @param Carbon $start
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function accountReport(Collection $accounts, Collection $expense, Carbon $start, Carbon $end)
{
if ($end < $start) {
return view('error')->with('message', trans('firefly.end_after_start_date')); // @codeCoverageIgnore
}
if ($start < session('first')) {
$start = session('first');
}
View::share(
'subTitle', trans(
'firefly.report_account',
['start' => $start->formatLocalized($this->monthFormat), 'end' => $end->formatLocalized($this->monthFormat)]
)
);
$generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end);
$generator->setAccounts($accounts);
$generator->setExpense($expense);
$result = $generator->generate();
return $result;
}
/**
* @param Collection $accounts
* @param Carbon $start
* @param Carbon $end
*
* @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/ */
public function auditReport(Collection $accounts, Carbon $start, Carbon $end) public function auditReport(Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -109,6 +146,8 @@ class ReportController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/ */
public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{ {
@ -145,6 +184,8 @@ class ReportController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/ */
public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
{ {
@ -180,6 +221,8 @@ class ReportController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/ */
public function defaultReport(Collection $accounts, Carbon $start, Carbon $end) public function defaultReport(Collection $accounts, Carbon $start, Carbon $end)
{ {
@ -230,6 +273,8 @@ class ReportController extends Controller
* @param string $reportType * @param string $reportType
* *
* @return mixed * @return mixed
*
* @throws \Throwable
*/ */
public function options(string $reportType) public function options(string $reportType)
{ {
@ -246,6 +291,9 @@ class ReportController extends Controller
case 'tag': case 'tag':
$result = $this->tagReportOptions(); $result = $this->tagReportOptions();
break; break;
case 'account':
$result = $this->accountReportOptions();
break;
} }
return Response::json(['html' => $result]); return Response::json(['html' => $result]);
@ -255,6 +303,8 @@ class ReportController extends Controller
* @param ReportFormRequest $request * @param ReportFormRequest $request
* *
* @return RedirectResponse|\Illuminate\Routing\Redirector * @return RedirectResponse|\Illuminate\Routing\Redirector
*
* @throws \FireflyIII\Exceptions\FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function postIndex(ReportFormRequest $request) public function postIndex(ReportFormRequest $request)
@ -267,6 +317,7 @@ class ReportController extends Controller
$categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); $categories = join(',', $request->getCategoryList()->pluck('id')->toArray());
$budgets = join(',', $request->getBudgetList()->pluck('id')->toArray()); $budgets = join(',', $request->getBudgetList()->pluck('id')->toArray());
$tags = join(',', $request->getTagList()->pluck('tag')->toArray()); $tags = join(',', $request->getTagList()->pluck('tag')->toArray());
$expense = join(',', $request->getExpenseList()->pluck('id')->toArray());
$uri = route('reports.index'); $uri = route('reports.index');
if (0 === $request->getAccountList()->count()) { if (0 === $request->getAccountList()->count()) {
@ -314,6 +365,9 @@ class ReportController extends Controller
case 'tag': case 'tag':
$uri = route('reports.report.tag', [$accounts, $tags, $start, $end]); $uri = route('reports.report.tag', [$accounts, $tags, $start, $end]);
break; break;
case 'account':
$uri = route('reports.report.account', [$accounts, $expense, $start, $end]);
break;
} }
return redirect($uri); return redirect($uri);
@ -326,6 +380,8 @@ class ReportController extends Controller
* @param Carbon $end * @param Carbon $end
* *
* @return string * @return string
*
* @throws \FireflyIII\Exceptions\FireflyException
*/ */
public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end)
{ {
@ -357,6 +413,32 @@ class ReportController extends Controller
/** /**
* @return string * @return string
*
* @throws \Throwable
*/
private function accountReportOptions(): string
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$expense = $repository->getActiveAccountsByType([AccountType::EXPENSE]);
$revenue = $repository->getActiveAccountsByType([AccountType::REVENUE]);
$set = new Collection;
$names = $revenue->pluck('name')->toArray();
foreach ($expense as $exp) {
if (in_array($exp->name, $names)) {
$set->push($exp);
}
}
$result = view('reports.options.account', compact('set'))->render();
return $result;
}
/**
* @return string
*
* @throws \Throwable
*/ */
private function budgetReportOptions(): string private function budgetReportOptions(): string
{ {
@ -370,6 +452,8 @@ class ReportController extends Controller
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
private function categoryReportOptions(): string private function categoryReportOptions(): string
{ {
@ -383,6 +467,8 @@ class ReportController extends Controller
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
private function noReportOptions(): string private function noReportOptions(): string
{ {
@ -391,6 +477,8 @@ class ReportController extends Controller
/** /**
* @return string * @return string
*
* @throws \Throwable
*/ */
private function tagReportOptions(): string private function tagReportOptions(): string
{ {

Some files were not shown because too many files have changed in this diff Show More