Compare commits

...

145 Commits

Author SHA1 Message Date
James Cole
735222a8ed Merge branch 'release/4.7.0' 2018-01-31 07:14:15 +01:00
James Cole
66f299cd06 Update composer file. 2018-01-31 06:42:44 +01:00
James Cole
e3c5268143 Update changelog for Sandstorm. 2018-01-31 06:35:41 +01:00
James Cole
df32493d77 Final update for some translations. 2018-01-30 21:07:14 +01:00
James Cole
86faf44153 Small fixes in change log [skip ci] 2018-01-29 20:17:43 +01:00
James Cole
e2f3e4b555 Update sandstorm file list. 2018-01-29 19:56:57 +01:00
James Cole
e57ed6015c Updated composer lock file. 2018-01-29 19:31:36 +01:00
James Cole
9c34ca7fc4 Changelog for Sandstorm. 2018-01-29 19:14:58 +01:00
James Cole
7b94f4a441 Update change log. 2018-01-29 19:13:51 +01:00
James Cole
693f8d0738 Update language files. 2018-01-29 19:13:43 +01:00
James Cole
d6ecbc06bf Add Portuguese (Brazil) 2018-01-29 19:12:58 +01:00
James Cole
c31674fffc New version in Sandstorm file. 2018-01-29 19:10:29 +01:00
James Cole
b08de8cc00 Clean up config. 2018-01-29 19:09:52 +01:00
James Cole
4c503e4c7c Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii:
  Workaround IE tab order issue w/ js initial select
2018-01-29 19:09:13 +01:00
James Cole
dd4158c6b4 Expand tests 2018-01-29 19:09:00 +01:00
James Cole
5fd7ea2b96 Add code coverage. 2018-01-29 19:08:49 +01:00
James Cole
b9ff80eb5a Update changelog. 2018-01-29 19:08:26 +01:00
James Cole
afc725bbc8 Merge pull request #1148 from devlearner/login-tab-order
Workaround IE tab order issue on js initial select
2018-01-28 21:13:56 +01:00
James Cole
0342c371cc Fix debug issue caught by @devlearner. 2018-01-28 20:59:26 +01:00
devlearner
2da4e6b048 Workaround IE tab order issue w/ js initial select 2018-01-28 14:23:06 +00:00
James Cole
f50550d79c Fix unit tests 2018-01-25 20:38:50 +01:00
James Cole
3fa39a6805 Clean up view and route. 2018-01-25 19:21:58 +01:00
James Cole
3dbe6d4870 Clean up config. 2018-01-25 19:21:46 +01:00
James Cole
59c48268ab Support more icons. 2018-01-25 19:21:40 +01:00
James Cole
1f83c5195d Add view method. Clean up repository links 2018-01-25 19:21:31 +01:00
James Cole
49a95a08fe More friendly demo user message. 2018-01-25 19:02:14 +01:00
James Cole
c86c5ccfe9 Add forgotten migration [skip ci] 2018-01-25 18:55:31 +01:00
James Cole
8a2497fc67 Expand mime type and upgrade version [skip ci] 2018-01-25 18:41:51 +01:00
James Cole
7c70732247 Some light refactoring. No changes. 2018-01-25 18:41:27 +01:00
James Cole
53fc4f2740 Better link to Spectre docs. [skip ci] 2018-01-24 15:23:27 +01:00
James Cole
f3ade5621e Merge pull request #1145 from devlearner/woff2
Include Woff fonts as well
2018-01-24 14:30:57 +01:00
devlearner
ec2e08e33a Update fonts for Source Sans Pro
(Bold Italic)
2018-01-24 21:04:03 +08:00
devlearner
b9a26faa4d Update css for Source Sans Pro
(Bold Italic)
2018-01-24 12:52:48 +00:00
James Cole
1a434d0c83 Expand readme with links to contribution pages. [skip ci] 2018-01-24 12:00:41 +01:00
James Cole
9a2c6c2967 Expand test coverage. 2018-01-24 11:09:21 +01:00
James Cole
602b35d589 Breadcrumb shows the correct title. 2018-01-24 11:09:05 +01:00
James Cole
f42cd0c7c3 Give correct info about import jobs. 2018-01-24 11:08:50 +01:00
James Cole
cb81855a17 Fix #1143 2018-01-24 11:05:00 +01:00
James Cole
89cf351ad0 add some fixes for #1111 2018-01-24 05:17:26 +01:00
devlearner
3f70a3f06c Added woff fonts 2018-01-24 01:10:04 +08:00
devlearner
9df360f010 Updated woff2 fonts
Roboto: Last modified 2017-10-16 (v18)
Lato: Last modified 2017-10-11 (v14)
2018-01-24 00:59:27 +08:00
James Cole
9a26d6d49f Fix #1140 2018-01-22 18:37:59 +01:00
James Cole
bc4d801c12 Fix #1141 2018-01-22 18:16:50 +01:00
James Cole
f2d8e13576 Fix #1142 2018-01-22 18:14:30 +01:00
James Cole
ec0b5db973 Update change log. 2018-01-21 20:04:53 +01:00
James Cole
46a0d1ce35 Update change log. 2018-01-21 20:03:29 +01:00
James Cole
cb81446ca6 Clean up debug code 2018-01-21 20:03:13 +01:00
James Cole
9350b4939c Make some file names lowercase. 2018-01-21 19:47:19 +01:00
James Cole
33e8c8c415 Make some file names lowercase. 2018-01-21 19:46:56 +01:00
James Cole
48fa86cc54 Improve some test coverage. 2018-01-21 18:06:57 +01:00
James Cole
d5e6d1c578 Remove reference to website from read me. 2018-01-21 11:10:08 +01:00
James Cole
0bc688795a Small update in update routine. 2018-01-21 11:09:55 +01:00
James Cole
eb76ed5591 New text in contributing [skip ci] 2018-01-21 09:10:36 +01:00
James Cole
839cbaf37a Updated read me file [skip ci] 2018-01-21 00:08:14 +01:00
James Cole
788fc9204d Update readme [skip ci] 2018-01-21 00:06:40 +01:00
James Cole
3e3e304ef3 Updated read me file [skip ci] 2018-01-20 23:59:10 +01:00
James Cole
447d453fdc Update composer lock file [skip ci] 2018-01-20 07:15:39 +01:00
James Cole
36fd7884f3 Update demo pages. 2018-01-20 07:15:26 +01:00
James Cole
54da08b2f2 Change settings so demo user can use Spectre. 2018-01-20 06:40:23 +01:00
James Cole
3f02072ae9 Update read me[skip ci] 2018-01-20 06:40:05 +01:00
James Cole
a9c117703b Update composer.json 2018-01-19 08:35:25 +01:00
James Cole
c137255155 Update .scrutinizer.yml 2018-01-19 08:23:31 +01:00
James Cole
e7829ecc38 Committed bad help JS. [skip ci] 2018-01-17 14:26:31 +01:00
James Cole
529bdafa31 Code climate file. 2018-01-17 13:04:43 +01:00
James Cole
e2af0caa41 Fix tests 2018-01-17 12:29:00 +01:00
James Cole
80f96abf08 Fix notes in link types. 2018-01-17 10:40:44 +01:00
James Cole
70da38193f Fix issue with budget chart. 2018-01-17 10:17:49 +01:00
James Cole
13df973873 Fix query cache. 2018-01-17 10:03:47 +01:00
James Cole
3ccb791674 Various code cleanup. [skip ci] 2018-01-17 09:32:18 +01:00
James Cole
ccf1a6c182 Fix #1134 2018-01-17 09:22:45 +01:00
James Cole
493543c1f5 Update language files [skip ci] 2018-01-17 06:43:04 +01:00
James Cole
5f5725e0e3 Explicit language tag in layout 2018-01-17 06:25:32 +01:00
James Cole
107dd42957 Update English language files [skip ci] 2018-01-17 06:24:50 +01:00
James Cole
a9f3fe4d3a Remove memcached experiment. 2018-01-16 22:01:55 +01:00
James Cole
3e62e17b9e Add some echo to Sandstorm scripts. 2018-01-16 21:34:36 +01:00
James Cole
57855b1930 Remove references to unused cache thing. 2018-01-16 21:09:27 +01:00
James Cole
aa9e8227bb Smal changes in Sandstorm configuration. [skip ci] #1130 2018-01-15 17:48:20 +01:00
James Cole
a80f083b6e Catch errors in DB seeds. 2018-01-15 17:13:23 +01:00
James Cole
474e066d4a Expand debug message [skip ci] 2018-01-14 19:59:05 +01:00
James Cole
4428ccefbf Expand debug message [skip ci] 2018-01-14 19:58:39 +01:00
James Cole
d568a6c8a9 First version of actual update check. 2018-01-14 19:56:18 +01:00
James Cole
97e9ad6cb2 Small updates in read me. 2018-01-14 19:49:29 +01:00
James Cole
00607d2a6d Code for #1040 2018-01-14 19:36:24 +01:00
James Cole
c2a425121d Code for #1040 2018-01-14 16:32:26 +01:00
James Cole
435694e9ea Code for #989 2018-01-14 10:57:27 +01:00
James Cole
f59135a9ca Code for #989 2018-01-14 10:48:17 +01:00
James Cole
102b106402 Different “drop up” menu. 2018-01-13 18:52:06 +01:00
James Cole
5c27c8e633 Multi currency net worth box. 2018-01-13 18:40:28 +01:00
James Cole
edd5215b21 Different icon for view. [skip ci] 2018-01-13 18:07:25 +01:00
James Cole
94b173ae6b New language strings. 2018-01-13 18:02:41 +01:00
James Cole
7d96b281b6 Add buttons to views 2018-01-13 18:01:53 +01:00
James Cole
a5515ac89f Update tests. 2018-01-13 10:36:49 +01:00
James Cole
fb863b0bf2 Improve step count for spectre imports. 2018-01-13 07:52:35 +01:00
James Cole
50882f309b Make sure number of steps is always correct. 2018-01-13 07:36:44 +01:00
James Cole
ce854fbb43 Catch error when trying to read (non-existent) logs. 2018-01-12 21:43:04 +01:00
James Cole
6799268ec4 Add intval just in case. 2018-01-12 21:31:39 +01:00
James Cole
6fe5ce0485 Expand budget report #1106 2018-01-12 21:08:59 +01:00
James Cole
cbeaf8e16a Expand tag report #1106 2018-01-12 21:02:27 +01:00
James Cole
04de4c9b36 Expand category report #1106 2018-01-12 20:53:18 +01:00
James Cole
517731cb59 Extra buttons 2018-01-12 20:37:56 +01:00
James Cole
e34e43173c Fix #1132 2018-01-12 20:37:39 +01:00
James Cole
79d6055a78 Fix #1131 2018-01-12 20:32:09 +01:00
James Cole
7ac4d2a2f4 Various new strings [skip ci] 2018-01-12 18:44:59 +01:00
James Cole
4984eda320 Clean up view HTML 2018-01-12 18:42:48 +01:00
James Cole
89e0791e2f Add button to create transaction. 2018-01-12 18:42:25 +01:00
James Cole
922d487821 Update icon [skip ci] 2018-01-11 21:27:24 +01:00
James Cole
4b789979ac Fix 2FA check #1125 2018-01-11 20:56:42 +01:00
James Cole
554b38ccff Code for #1126 2018-01-11 20:49:55 +01:00
James Cole
9614310208 Extra button for #1124 2018-01-11 19:08:01 +01:00
James Cole
d9ec3ac354 Code for #1124 2018-01-11 19:06:46 +01:00
James Cole
f326f08f7b Fix #1088 2018-01-11 18:58:33 +01:00
James Cole
0ae8418f32 Fix tests. 2018-01-10 20:07:47 +01:00
James Cole
309f9cd076 Add new roles 2018-01-10 19:59:40 +01:00
James Cole
61f5ed3874 Fix check for column roles. 2018-01-10 19:06:27 +01:00
James Cole
91178d2604 Various cleanup in import. 2018-01-10 18:18:49 +01:00
James Cole
87dae6ea18 Expand some code for Spectre import. 2018-01-10 16:49:32 +01:00
James Cole
2e495c38d1 Make env files more readable. 2018-01-10 14:37:40 +01:00
James Cole
c2987aaf4c Update docker config #1081 2018-01-10 13:15:12 +01:00
James Cole
f4f4eecb7b Add routes to ignore. [skip ci] 2018-01-10 12:42:32 +01:00
James Cole
84d9287251 Remove unused route [skip ci] 2018-01-10 12:42:17 +01:00
James Cole
b71f334744 Small rewrite in readme [skip ci] 2018-01-10 12:42:01 +01:00
James Cole
ad306e4d01 Strings for #1116 (help text) 2018-01-10 08:03:37 +01:00
James Cole
e40d4ef829 Add strings for #1116 to intro page. 2018-01-10 07:57:36 +01:00
James Cole
48c16c3dcc Clean up binders. 2018-01-10 07:51:47 +01:00
James Cole
c045193246 Remove unnecessary routes. 2018-01-10 07:29:55 +01:00
James Cole
a816e59a97 Small update in readme [skip ci] 2018-01-10 07:19:28 +01:00
James Cole
892074eaf2 Final touches on readme [skipci] 2018-01-09 20:24:45 +01:00
James Cole
3e501e429d Fix link to softalucous [skip ci] 2018-01-09 20:23:31 +01:00
James Cole
0f40accb4c Expand links. [skipci] 2018-01-09 20:22:46 +01:00
James Cole
3dae6c99a4 Fix badges. [skipci] 2018-01-09 20:17:58 +01:00
James Cole
b185c83597 Update read me file. [skip ci] 2018-01-09 20:16:17 +01:00
James Cole
ef6b4120f1 Lower case some file names. 2018-01-09 19:27:12 +01:00
James Cole
a43eef01fc Lower case some file names. 2018-01-09 19:26:49 +01:00
James Cole
2cb9aa537f Experimental support for CodeCov. 2018-01-09 17:52:18 +01:00
James Cole
2edd49a8b4 First version that supports Spectre. 2018-01-08 20:20:45 +01:00
James Cole
a57554d380 Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii:
  Fix charts in IE
2018-01-08 20:20:14 +01:00
James Cole
c89486b6d9 Expand Spectre import code. 2018-01-08 19:21:00 +01:00
James Cole
f737cb7235 Update language files for #1109 2018-01-08 19:20:41 +01:00
James Cole
f1fe169553 Disable Spectre again. 2018-01-08 19:19:17 +01:00
James Cole
2fc760780e Add support for Russian. 2018-01-08 19:19:03 +01:00
James Cole
8c3290bf6f Merge pull request #1107 from devlearner/patch-1
Fix charts in Internet Explorer
2018-01-08 10:21:09 +01:00
devlearner
495158b9c9 Fix charts in IE
since IE apparently doesn't support arrow function expression (and throws a syntax error)
2018-01-08 07:49:32 +00:00
James Cole
f9fc9b1889 Spectre should not be enabled. [skip ci] 2018-01-07 16:49:49 +01:00
James Cole
11ff2ab9d1 Debug controller cannot be accessed by demo user. 2018-01-07 12:53:20 +01:00
James Cole
52b138e6b2 Fix error in read me [skip ci] 2018-01-07 12:43:02 +01:00
392 changed files with 10159 additions and 3181 deletions

12
.codeclimate.yml Normal file
View File

@@ -0,0 +1,12 @@
---
exclude_patterns:
- public/lib/
- public/js/lib/
- public/fonts/
- public/css/jquery-ui/
- public/css/bootstrap-multiselect.css
- public/css/bootstrap-sortable.css
- public/css/bootstrap-tagsinput.css
- public/css/daterangepicker.css
- public/css/google-fonts.css
- .sandstorm/

View File

@@ -1,12 +1,24 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=${FF_APP_ENV}
APP_DEBUG=false
APP_NAME=FireflyIII
APP_KEY=${FF_APP_KEY}
APP_LOG=daily
APP_LOG_LEVEL=warning
APP_URL=http://localhost
TRUSTED_PROXIES=
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
# This should be your email address
SITE_OWNER=mail@example.com
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=${FF_APP_KEY}
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
APP_URL=${APP_URL}
TRUSTED_PROXIES=${TRUSTED_PROXIES}
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
DB_CONNECTION=mysql
DB_HOST=${FF_DB_HOST}
DB_PORT=3306
@@ -14,19 +26,27 @@ DB_DATABASE=${FF_DB_NAME}
DB_USERNAME=${FF_DB_USER}
DB_PASSWORD=${FF_DB_PASSWORD}
BROADCAST_DRIVER=log
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
APP_LOG=daily
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=warning
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# If you want Firefly III to mail you, update these settings
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=
SITE_OWNER=mail@example.com
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=true
IS_SANDSTORM=false
IS_HEROKU=false

View File

@@ -1,12 +1,24 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=local
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
APP_NAME=FireflyIII
# This should be your email address
SITE_OWNER=mail@example.com
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=SomeRandomStringOf32CharsExactly
APP_LOG=daily
APP_LOG_LEVEL=notice
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
APP_URL=http://localhost
TRUSTED_PROXIES=
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
@@ -14,19 +26,27 @@ DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
BROADCAST_DRIVER=log
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
APP_LOG=daily
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=notice
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# If you want Firefly III to mail you, update these settings
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=
SITE_OWNER=mail@example.com
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
IS_HEROKU=false

View File

@@ -1,12 +1,24 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=heroku
APP_DEBUG=true
APP_NAME=FireflyIII
APP_KEY=7ahyYVPVsmxjdhsweWCauGeJfwc92NP2
APP_LOG=errorlog
APP_LOG_LEVEL=debug
APP_URL=http://localhost
TRUSTED_PROXIES=*
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
# This should be your email address
SITE_OWNER=heroku@example.com
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=7ahyYVPVsmxjdhsweWCauGeJfwc92NP2
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
APP_URL=http://localhost
TRUSTED_PROXIES=
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
DB_CONNECTION=pgsql
@@ -14,19 +26,27 @@ DB_CONNECTION=pgsql
BROADCAST_DRIVER=log
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
APP_LOG=errorlog
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=debug
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# If you want Firefly III to mail you, update these settings
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=
SITE_OWNER=heroku@example.com
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=false
IS_HEROKU=true

View File

@@ -1,12 +1,24 @@
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=local
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false
APP_NAME=FireflyIII
# This should be your email address
SITE_OWNER=sandstorm@example.com
# The encryption key for your database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=SomeRandomStringOf32CharsExactly
APP_LOG=syslog
APP_LOG_LEVEL=info
# APP_URL and TRUSTED_PROXIES are useful when using Docker and/or a reverse proxy.
APP_URL=http://localhost
TRUSTED_PROXIES=
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# If you use SQLite, set connection to `sqlite` and remove the database, username and password settings.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
@@ -14,19 +26,27 @@ DB_DATABASE=firefly
DB_USERNAME=firefly
DB_PASSWORD=firefly
BROADCAST_DRIVER=log
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog' and 'errorlog' which will log to the system itself.
APP_LOG=syslog
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=info
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
# Cookie settings. Should not be necessary to change these.
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# If you want Firefly III to mail you, update these settings
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
@@ -35,26 +55,36 @@ MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
# Firefly III can send you the following messages
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
# Set a Mapbox API key here (see mapbox.com) so there might be a map available at various places.
MAPBOX_API_KEY=
# If you wish to track your own behavior over Firefly III, set a valid analytics tracker ID here.
ANALYTICS_ID=
SITE_OWNER=mail@example.com
# Most parts of the database are encrypted by default, but you can turn this off if you want to.
# This makes it easier to migrate your database. Not that some fields will never be decrypted.
USE_ENCRYPTION=true
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
APP_NAME=FireflyIII
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_PREFIX=firefly
SEARCH_RESULT_LIMIT=50
EXCHANGE_RATE_SERVICE=fixerio
PUSHER_KEY=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
IS_DOCKER=false
IS_SANDSTORM=true
IS_HEROKU=false

View File

@@ -4,7 +4,7 @@
## 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.org/requested-features/).
I am always interested in expanding Firefly III's many features. Just open a ticket or [drop me a line](mailto:thegrumpydictator@gmail.com).
## Pull requests

View File

@@ -3,10 +3,9 @@
# This script only runs once, when the app connects to sandstorm.
set -euo pipefail
echo "In build.sh"
cd /opt/app
cp .env.sandstorm .env
if [ -f /opt/app/composer.json ] ; then

View File

@@ -1,3 +1,32 @@
# 4.7.0
- Support for Russian and Portuguese (Brazil)
- Support for the Spectre API (Salt Edge)
- Many strings now translatable thanks to [Nik-vr](https://github.com/Nik-vr) ([issue 1118](https://github.com/firefly-iii/firefly-iii/issues/1118), [issue 1116](https://github.com/firefly-iii/firefly-iii/issues/1116), [issue 1109](https://github.com/firefly-iii/firefly-iii/issues/1109), )
- Many buttons to quickly create stuff
- Sum of tables in reports, requested by [MacPaille](https://github.com/MacPaille) ([issue 1106](https://github.com/firefly-iii/firefly-iii/issues/1106))
- Future versions of Firefly III will notify you there is a new version, as suggested by [8bitgentleman](https://github.com/8bitgentleman) in [issue 1050](https://github.com/firefly-iii/firefly-iii/issues/1050)
- Improved net worth box [issue 1101](https://github.com/firefly-iii/firefly-iii/issues/1101) ([Nik-vr](https://github.com/Nik-vr))
- Nice dropdown in transaction list [issue 1082](https://github.com/firefly-iii/firefly-iii/issues/1082)
- Better support for local fonts thanks to [devlearner](https://github.com/devlearner) ([issue 1145](https://github.com/firefly-iii/firefly-iii/issues/1145))
- Improve attachment support and view capabilities (suggested by [trinhit](https://github.com/trinhit) in [issue 1146](https://github.com/firefly-iii/firefly-iii/issues/1146))
- Whole new [read me file](https://github.com/firefly-iii/firefly-iii/blob/master/readme.md), [new end user documentation](https://firefly-iii.readthedocs.io/en/latest/) and an [updated website](https://www.firefly-iii.org/)!
- Many charts and info-blocks now scale property ([issue 989](https://github.com/firefly-iii/firefly-iii/issues/989) and [issue 1040](https://github.com/firefly-iii/firefly-iii/issues/1040))
- Charts work in IE thanks to [devlearner](https://github.com/devlearner) ([issue 1107](https://github.com/firefly-iii/firefly-iii/issues/1107))
- Various fixes in import routine
- Bug that left charts empty ([issue 1088](https://github.com/firefly-iii/firefly-iii/issues/1088)), reported by various users amongst which [jinformatique](https://github.com/jinformatique)
- [Issue 1124](https://github.com/firefly-iii/firefly-iii/issues/1124), as reported by [gavu](https://github.com/gavu)
- [Issue 1125](https://github.com/firefly-iii/firefly-iii/issues/1125), as reported by [gavu](https://github.com/gavu)
- [Issue 1126](https://github.com/firefly-iii/firefly-iii/issues/1126), as reported by [gavu](https://github.com/gavu)
- [Issue 1131](https://github.com/firefly-iii/firefly-iii/issues/1131), as reported by [dp87](https://github.com/dp87)
- [Issue 1129](https://github.com/firefly-iii/firefly-iii/issues/1129), as reported by [gavu](https://github.com/gavu)
- [Issue 1132](https://github.com/firefly-iii/firefly-iii/issues/1132), as reported by [gavu](https://github.com/gavu)
- Issue with cache in Sandstorm ([issue 1130](https://github.com/firefly-iii/firefly-iii/issues/1130))
- [Issue 1134](https://github.com/firefly-iii/firefly-iii/issues/1134)
- [Issue 1140](https://github.com/firefly-iii/firefly-iii/issues/1140)
- [Issue 1141](https://github.com/firefly-iii/firefly-iii/issues/1141), reported by [ErikFontanel](https://github.com/ErikFontanel)
- [Issue 1142](https://github.com/firefly-iii/firefly-iii/issues/1142)
- Removed many access rights from the demo user
# 4.6.13
- [Issue 1074](https://github.com/firefly-iii/firefly-iii/issues/1074), suggested by [MacPaille](https://github.com/MacPaille)
- [Issue 1077](https://github.com/firefly-iii/firefly-iii/issues/1077), suggested by [wtercato](https://github.com/wtercato)

View File

@@ -1,6 +1,7 @@
#!/bin/bash
# Runs every time we create a new grain!
echo "Now in launcher.sh"
# Create a bunch of folders under the clean /var that php, nginx, and mysql expect to exist
mkdir -p /var/lib/mysql
@@ -30,7 +31,6 @@ mkdir -p /var/storage/framework/views
mkdir -p /var/storage/logs
mkdir -p /var/storage/upload
# Ensure mysql tables created
HOME=/etc/mysql /usr/bin/mysql_install_db --force
@@ -58,5 +58,9 @@ echo "Migrating..."
php /opt/app/artisan migrate --seed --force
echo "Done!"
echo "Clear cache.."
php /opt/app/artisan cache:clear
echo "Done"
# Start nginx.
/usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;"

View File

@@ -200,6 +200,7 @@ lib/x86_64-linux-gnu/libwrap.so.0.7.6
lib/x86_64-linux-gnu/libz.so.1
lib/x86_64-linux-gnu/libz.so.1.2.8
lib64/ld-linux-x86-64.so.2
opt/app/.codeclimate.yml
opt/app/.env
opt/app/.env.docker
opt/app/.env.example
@@ -237,10 +238,7 @@ opt/app/.sandstorm/service-config/mime.types
opt/app/.sandstorm/service-config/nginx.conf
opt/app/.sandstorm/setup.sh
opt/app/.sandstorm/stack
opt/app/CHANGELOG.md
opt/app/CODE_OF_CONDUCT.md
opt/app/LICENSE
opt/app/README.md
opt/app/app.json
opt/app/app/Console/Commands/CreateExport.php
opt/app/app/Console/Commands/CreateImport.php
@@ -644,6 +642,7 @@ opt/app/app/Services/Spectre/Object/Login.php
opt/app/app/Services/Spectre/Object/SpectreObject.php
opt/app/app/Services/Spectre/Object/Token.php
opt/app/app/Services/Spectre/Object/Transaction.php
opt/app/app/Services/Spectre/Object/TransactionExtra.php
opt/app/app/Services/Spectre/Request/CreateTokenRequest.php
opt/app/app/Services/Spectre/Request/ListAccountsRequest.php
opt/app/app/Services/Spectre/Request/ListCustomersRequest.php
@@ -687,7 +686,6 @@ opt/app/app/Support/Preferences.php
opt/app/app/Support/Search/Modifier.php
opt/app/app/Support/Search/Search.php
opt/app/app/Support/Search/SearchInterface.php
opt/app/app/Support/SingleCacheProperties.php
opt/app/app/Support/Steam.php
opt/app/app/Support/Twig/AmountFormat.php
opt/app/app/Support/Twig/Extension/Transaction.php
@@ -762,6 +760,7 @@ opt/app/artisan
opt/app/bootstrap/app.php
opt/app/bootstrap/cache/packages.php
opt/app/bootstrap/cache/services.php
opt/app/changelog.md
opt/app/composer.json
opt/app/composer.lock
opt/app/composer.phar
@@ -829,56 +828,121 @@ opt/app/public/css/jquery-ui/jquery-ui.theme.min.css
opt/app/public/favicon-16x16.png
opt/app/public/favicon-32x32.png
opt/app/public/favicon.ico
opt/app/public/fonts/SourceSansPro-Bold-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-Bold-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-Bold-cyrillic.woff
opt/app/public/fonts/SourceSansPro-Bold-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-Bold-greek-ext.woff
opt/app/public/fonts/SourceSansPro-Bold-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-Bold-greek.woff
opt/app/public/fonts/SourceSansPro-Bold-greek.woff2
opt/app/public/fonts/SourceSansPro-Bold-latin-ext.woff
opt/app/public/fonts/SourceSansPro-Bold-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-Bold-latin.woff
opt/app/public/fonts/SourceSansPro-Bold-latin.woff2
opt/app/public/fonts/SourceSansPro-Bold-vietnamese.woff
opt/app/public/fonts/SourceSansPro-Bold-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-cyrillic.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-greek-ext.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-greek.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-greek.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-latin-ext.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-latin.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-latin.woff2
opt/app/public/fonts/SourceSansPro-BoldItalic-vietnamese.woff
opt/app/public/fonts/SourceSansPro-BoldItalic-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-Italic-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-Italic-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-Italic-cyrillic.woff
opt/app/public/fonts/SourceSansPro-Italic-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-Italic-greek-ext.woff
opt/app/public/fonts/SourceSansPro-Italic-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-Italic-greek.woff
opt/app/public/fonts/SourceSansPro-Italic-greek.woff2
opt/app/public/fonts/SourceSansPro-Italic-latin-ext.woff
opt/app/public/fonts/SourceSansPro-Italic-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-Italic-latin.woff
opt/app/public/fonts/SourceSansPro-Italic-latin.woff2
opt/app/public/fonts/SourceSansPro-Italic-vietnamese.woff
opt/app/public/fonts/SourceSansPro-Italic-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-Light-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-Light-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-Light-cyrillic.woff
opt/app/public/fonts/SourceSansPro-Light-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-Light-greek-ext.woff
opt/app/public/fonts/SourceSansPro-Light-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-Light-greek.woff
opt/app/public/fonts/SourceSansPro-Light-greek.woff2
opt/app/public/fonts/SourceSansPro-Light-latin-ext.woff
opt/app/public/fonts/SourceSansPro-Light-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-Light-latin.woff
opt/app/public/fonts/SourceSansPro-Light-latin.woff2
opt/app/public/fonts/SourceSansPro-Light-vietnamese.woff
opt/app/public/fonts/SourceSansPro-Light-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-LightItalic-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-cyrillic.woff
opt/app/public/fonts/SourceSansPro-LightItalic-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-greek-ext.woff
opt/app/public/fonts/SourceSansPro-LightItalic-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-greek.woff
opt/app/public/fonts/SourceSansPro-LightItalic-greek.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-latin-ext.woff
opt/app/public/fonts/SourceSansPro-LightItalic-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-latin.woff
opt/app/public/fonts/SourceSansPro-LightItalic-latin.woff2
opt/app/public/fonts/SourceSansPro-LightItalic-vietnamese.woff
opt/app/public/fonts/SourceSansPro-LightItalic-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-Regular-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-Regular-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-Regular-cyrillic.woff
opt/app/public/fonts/SourceSansPro-Regular-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-Regular-greek-ext.woff
opt/app/public/fonts/SourceSansPro-Regular-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-Regular-greek.woff
opt/app/public/fonts/SourceSansPro-Regular-greek.woff2
opt/app/public/fonts/SourceSansPro-Regular-latin-ext.woff
opt/app/public/fonts/SourceSansPro-Regular-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-Regular-latin.woff
opt/app/public/fonts/SourceSansPro-Regular-latin.woff2
opt/app/public/fonts/SourceSansPro-Regular-vietnamese.woff
opt/app/public/fonts/SourceSansPro-Regular-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBold-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-cyrillic.woff
opt/app/public/fonts/SourceSansPro-SemiBold-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-greek-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBold-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-greek.woff
opt/app/public/fonts/SourceSansPro-SemiBold-greek.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-latin-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBold-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-latin.woff
opt/app/public/fonts/SourceSansPro-SemiBold-latin.woff2
opt/app/public/fonts/SourceSansPro-SemiBold-vietnamese.woff
opt/app/public/fonts/SourceSansPro-SemiBold-vietnamese.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-cyrillic-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-cyrillic-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-cyrillic.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-cyrillic.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-greek-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-greek-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-greek.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-greek.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-latin-ext.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-latin-ext.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-latin.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-latin.woff2
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-vietnamese.woff
opt/app/public/fonts/SourceSansPro-SemiBoldItalic-vietnamese.woff2
opt/app/public/fonts/lato-100.woff
opt/app/public/fonts/lato-100.woff2
opt/app/public/fonts/roboto-light-300.woff
opt/app/public/fonts/roboto-light-300.woff2
opt/app/public/images/error.png
opt/app/public/images/image.png
@@ -1004,6 +1068,7 @@ opt/app/public/report.html
opt/app/public/robots.txt
opt/app/public/safari-pinned-tab.svg
opt/app/public/web.config
opt/app/readme.md
opt/app/resources/lang/de_DE/auth.php
opt/app/resources/lang/de_DE/bank.php
opt/app/resources/lang/de_DE/breadcrumbs.php
@@ -1092,6 +1157,34 @@ opt/app/resources/lang/pl_PL/list.php
opt/app/resources/lang/pl_PL/pagination.php
opt/app/resources/lang/pl_PL/passwords.php
opt/app/resources/lang/pl_PL/validation.php
opt/app/resources/lang/pt_BR/auth.php
opt/app/resources/lang/pt_BR/bank.php
opt/app/resources/lang/pt_BR/breadcrumbs.php
opt/app/resources/lang/pt_BR/config.php
opt/app/resources/lang/pt_BR/csv.php
opt/app/resources/lang/pt_BR/demo.php
opt/app/resources/lang/pt_BR/firefly.php
opt/app/resources/lang/pt_BR/form.php
opt/app/resources/lang/pt_BR/import.php
opt/app/resources/lang/pt_BR/intro.php
opt/app/resources/lang/pt_BR/list.php
opt/app/resources/lang/pt_BR/pagination.php
opt/app/resources/lang/pt_BR/passwords.php
opt/app/resources/lang/pt_BR/validation.php
opt/app/resources/lang/ru_RU/auth.php
opt/app/resources/lang/ru_RU/bank.php
opt/app/resources/lang/ru_RU/breadcrumbs.php
opt/app/resources/lang/ru_RU/config.php
opt/app/resources/lang/ru_RU/csv.php
opt/app/resources/lang/ru_RU/demo.php
opt/app/resources/lang/ru_RU/firefly.php
opt/app/resources/lang/ru_RU/form.php
opt/app/resources/lang/ru_RU/import.php
opt/app/resources/lang/ru_RU/intro.php
opt/app/resources/lang/ru_RU/list.php
opt/app/resources/lang/ru_RU/pagination.php
opt/app/resources/lang/ru_RU/passwords.php
opt/app/resources/lang/ru_RU/validation.php
opt/app/resources/lang/tr_TR/auth.php
opt/app/resources/lang/tr_TR/bank.php
opt/app/resources/lang/tr_TR/breadcrumbs.php
@@ -1169,7 +1262,6 @@ opt/app/resources/views/demo/accounts/index.twig
opt/app/resources/views/demo/budgets/index.twig
opt/app/resources/views/demo/currencies/index.twig
opt/app/resources/views/demo/home.twig
opt/app/resources/views/demo/import/configure.twig
opt/app/resources/views/demo/import/index.twig
opt/app/resources/views/demo/index.twig
opt/app/resources/views/demo/no-demo-text.twig
@@ -2962,6 +3054,7 @@ opt/app/vendor/league/flysystem/docs/adapter/aws-s3-v2.md
opt/app/vendor/league/flysystem/docs/adapter/aws-s3-v3.md
opt/app/vendor/league/flysystem/docs/adapter/azure.md
opt/app/vendor/league/flysystem/docs/adapter/copy.md
opt/app/vendor/league/flysystem/docs/adapter/digitalocean-spaces.md
opt/app/vendor/league/flysystem/docs/adapter/dropbox.md
opt/app/vendor/league/flysystem/docs/adapter/ftp.md
opt/app/vendor/league/flysystem/docs/adapter/gridfs.md
@@ -2981,10 +3074,15 @@ opt/app/vendor/league/flysystem/docs/creating-an-adapter.md
opt/app/vendor/league/flysystem/docs/index.md
opt/app/vendor/league/flysystem/docs/installation.md
opt/app/vendor/league/flysystem/docs/integrations.md
opt/app/vendor/league/flysystem/docs/logo/become_a_patron_button.png
opt/app/vendor/league/flysystem/docs/logo/become_a_patron_button@2x.png
opt/app/vendor/league/flysystem/docs/logo/become_a_patron_button@3x.png
opt/app/vendor/league/flysystem/docs/logo/laravel.svg
opt/app/vendor/league/flysystem/docs/mount-manager.md
opt/app/vendor/league/flysystem/docs/performance.md
opt/app/vendor/league/flysystem/docs/plugins.md
opt/app/vendor/league/flysystem/docs/recipes.md
opt/app/vendor/league/flysystem/docs/sponsors.md
opt/app/vendor/league/flysystem/docs/upgrade-to-1.0.0.md
opt/app/vendor/league/flysystem/src/Adapter/AbstractAdapter.php
opt/app/vendor/league/flysystem/src/Adapter/AbstractFtpAdapter.php
@@ -3032,6 +3130,7 @@ opt/app/vendor/league/flysystem/src/Util.php
opt/app/vendor/league/flysystem/src/Util/ContentListingFormatter.php
opt/app/vendor/league/flysystem/src/Util/MimeType.php
opt/app/vendor/league/flysystem/src/Util/StreamHasher.php
opt/app/vendor/league/flysystem/wait_for_ftp_service.php
opt/app/vendor/monolog/monolog/.php_cs
opt/app/vendor/monolog/monolog/CHANGELOG.md
opt/app/vendor/monolog/monolog/LICENSE
@@ -3413,6 +3512,9 @@ opt/app/vendor/ramsey/uuid/CONTRIBUTING.md
opt/app/vendor/ramsey/uuid/LICENSE
opt/app/vendor/ramsey/uuid/README.md
opt/app/vendor/ramsey/uuid/composer.json
opt/app/vendor/ramsey/uuid/docs/Makefile
opt/app/vendor/ramsey/uuid/docs/conf.py
opt/app/vendor/ramsey/uuid/docs/index.rst
opt/app/vendor/ramsey/uuid/src/BinaryUtils.php
opt/app/vendor/ramsey/uuid/src/Builder/DefaultUuidBuilder.php
opt/app/vendor/ramsey/uuid/src/Builder/DegradedUuidBuilder.php
@@ -4369,6 +4471,7 @@ opt/app/vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php
opt/app/vendor/symfony/debug/Tests/HeaderMock.php
opt/app/vendor/symfony/debug/Tests/MockExceptionHandler.php
opt/app/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt
opt/app/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt
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
@@ -4786,6 +4889,7 @@ opt/app/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php
opt/app/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php
@@ -4997,6 +5101,7 @@ opt/app/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php
opt/app/vendor/symfony/routing/Tests/Fixtures/CustomCompiledRoute.php
opt/app/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php
opt/app/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php
opt/app/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php
opt/app/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php
opt/app/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php
opt/app/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php

View File

@@ -15,8 +15,8 @@ const pkgdef :Spk.PackageDefinition = (
manifest = (
appTitle = (defaultText = "Firefly III"),
appVersion = 7,
appMarketingVersion = (defaultText = "4.6.13"),
appVersion = 8,
appMarketingVersion = (defaultText = "4.7.0"),
actions = [
# Define your "new document" handlers here.

View File

@@ -57,6 +57,7 @@ http {
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 900;
fastcgi_param QUERY_STRING $query_string;

View File

@@ -2,6 +2,7 @@
# When you change this file, you must take manual action. Read this doc:
# - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh
echo "Now in setup.sh"
set -euo pipefail
@@ -14,8 +15,11 @@ apt-get install -y python-software-properties software-properties-common
# install all languages
sed -i 's/# de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# id_ID.UTF-8 UTF-8/id_ID.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# nl_NL.UTF-8 UTF-8/nl_NL.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# pl_PL.UTF-8 UTF-8/pl_PL.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/g' /etc/locale.gen
sed -i 's/# tr_TR.UTF-8 UTF-8/tr_TR.UTF-8 UTF-8/g' /etc/locale.gen
dpkg-reconfigure --frontend=noninteractive locales

View File

@@ -1,51 +1,58 @@
# .scrutinizer.yml
tools:
external_code_coverage: false
filter:
paths:
---
build:
nodes:
analysis:
project_setup:
override: true
tests:
override:
- php-scrutinizer-run
checks:
javascript: true
php:
align_assignments: true
avoid_fixme_comments: true
avoid_multiple_statements_on_same_line: true
avoid_perl_style_comments: true
avoid_todo_comments: true
duplication: false
encourage_single_quotes: true
newline_at_end_of_file: true
no_goto: true
no_long_variable_names:
maximum: "20"
no_short_method_names:
minimum: "3"
no_short_variable_names:
minimum: "3"
optional_parameters_at_the_end: true
parameter_doc_comments: true
remove_extra_empty_lines: true
return_doc_comment_if_not_inferrable: true
return_doc_comments: true
uppercase_constants: true
use_self_instead_of_fqcn: true
coding_style:
php:
spaces:
around_operators:
concatenation: true
other:
after_type_cast: false
filter:
excluded_paths:
- database/migrations/*
- bootstrap/*
- config/*
- docker/*
- public/js/lib/*
- public/lib/adminlte/js/*
- public/lib/bootstrap/js/*
- resources/*
- routes/*
- storage/*
paths:
- app/*
- public/js/ff/*
excluded_paths:
- "database/migrations/*"
- "bootstrap/*"
- "config/*"
- "docker/*"
- "public/js/lib/*"
- "public/lib/adminlte/js/*"
- "public/lib/bootstrap/js/*"
- "resources/*"
- "routes/*"
- "storage/*"
checks:
php:
use_self_instead_of_fqcn: true
uppercase_constants: true
return_doc_comments: true
return_doc_comment_if_not_inferrable: true
remove_extra_empty_lines: true
parameter_doc_comments: true
optional_parameters_at_the_end: true
no_short_variable_names:
minimum: '3'
no_short_method_names:
minimum: '3'
no_long_variable_names:
maximum: '20'
no_goto: true
newline_at_end_of_file: true
encourage_single_quotes: true
avoid_todo_comments: true
avoid_perl_style_comments: true
avoid_fixme_comments: true
avoid_multiple_statements_on_same_line: true
align_assignments: true
duplication: false
javascript: true
coding_style:
php:
spaces:
around_operators:
concatenation: true
other:
after_type_cast: false
tools:
external_code_coverage: false

View File

@@ -24,6 +24,7 @@ script:
after_success:
- travis_retry php vendor/bin/php-coveralls -x storage/build/clover-all.xml
- bash <(curl -s https://codecov.io/bash) -f storage/build/clover-all.xml
# safelist
branches:

View File

@@ -1,89 +0,0 @@
# Firefly III: A personal finances manager
[![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://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)
[![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 supports the use of budgets. You can categorize and tag your transactions.
It also supports credit cards, shared household accounts and savings accounts.
There are many financial reports available.
## Want to try Firefly III?
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
### Using docker
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)
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
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/firefly-iii/firefly-iii/tree/master)
Register for a free Heroku account and instantly run Firefly III on your very own cloud instance.
### 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.
### Other options
Firefly III is also available as a package on [https://softaculous.com/](Softaculous) and [AMPPS](https://www.ampps.com/).
## More about Firefly III
Personal financial management is pretty difficult, and everybody has their own approach to it.
Some people make budgets, other people limit their cashflow by throwing away their credit cards,
others try to increase their current cashflow. There are tons of ways to save and earn money.
Firefly works on the principle that if you know where you're money is going, you can stop it from going there.
### Some advantages of using Firefly
- Firefly can import any CSV file, so migrating from other systems is easy.
- Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", its just somebody elses computer!
- Firefly has lots of features without being fancy or bloated.
- 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).
[You can read more about Firefly III, and its features, on the website](https://firefly-iii.org/).
### Contributing
Please read [CONTRIBUTING.md](https://github.com/firefly-iii/firefly-iii/blob/master/.github/CONTRIBUTING.md) for details on contributing, and the process for submitting pull requests. Please check out the [code of conduct](https://github.com/firefly-iii/firefly-iii/blob/master/CODE_OF_CONDUCT.md) as well.
### Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see [the tags](https://github.com/firefly-iii/firefly-iii/tags) on this repository.
### Authors
* James Cole
* Over time, [many people have contributed to Firefly III](https://github.com/firefly-iii/firefly-iii/graphs/contributors).
### License
This work [is licensed](https://github.com/firefly-iii/firefly-iii/blob/master/LICENSE) under the [GPL v3](https://www.gnu.org/licenses/gpl.html).
### Other stuff
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).
### 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)

View File

@@ -178,7 +178,7 @@ class CreateImport extends Command
$cwd = getcwd();
$validTypes = config('import.options.file.import_formats');
$type = strtolower($this->option('type'));
if (null === $user->id) {
if (null === $user) {
$this->error(sprintf('There is no user with ID %d.', $this->option('user')));
return false;

View File

@@ -220,7 +220,7 @@ class UpgradeDatabase extends Command
// when mismatch in transaction:
if (!(intval($transaction->transaction_currency_id) === intval($currency->id))) {
$transaction->foreign_currency_id = $transaction->transaction_currency_id;
$transaction->foreign_currency_id = intval($transaction->transaction_currency_id);
$transaction->foreign_amount = $transaction->amount;
$transaction->transaction_currency_id = $currency->id;
$transaction->save();
@@ -412,12 +412,10 @@ class UpgradeDatabase extends Command
if (!(intval($transaction->transaction_currency_id) === intval($currency->id)) && null === $transaction->foreign_amount) {
Log::debug(
sprintf(
'Transaction #%d has a currency setting (#%d) (%s) that should be #%d (%s). Amount remains %s, currency is changed.',
'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.',
$transaction->id,
$transaction->transaction_currency_id,
$this->var_dump_ret(intval($transaction->transaction_currency_id)),
$currency->id,
$this->var_dump_ret(intval($currency->id)),
$transaction->amount
)
);
@@ -504,19 +502,4 @@ class UpgradeDatabase extends Command
return;
}
/**
* @param null $mixed
*
* @return string
*/
private function var_dump_ret($mixed = null): string
{
ob_start();
var_dump($mixed);
$content = ob_get_contents();
ob_end_clean();
return trim($content);
}
}

View File

@@ -36,7 +36,7 @@ trait VerifiesAccessToken
/**
* Abstract method to make sure trait knows about method "option".
*
* @param null $key
* @param string|null $key
*
* @return mixed
*/
@@ -55,7 +55,7 @@ trait VerifiesAccessToken
$repository = app(UserRepositoryInterface::class);
$user = $repository->find($userId);
if (null === $user->id) {
if (null === $user) {
Log::error(sprintf('verifyAccessToken(): no such user for input "%d"', $userId));
return false;

View File

@@ -52,7 +52,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
*/
public function __construct()
{
// @var AttachmentRepositoryInterface repository
/** @var AttachmentRepositoryInterface repository */
$this->repository = app(AttachmentRepositoryInterface::class);
// make storage:
$this->uploadDisk = Storage::disk('upload');

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
use Steam;
@@ -173,7 +174,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
$startBalance = $dayBeforeBalance;
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
// @var Transaction $journal
/** @var Transaction $transaction */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;

View File

@@ -194,7 +194,9 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
*/
private function summarizeByBudget(Collection $collection): array
{
$result = [];
$result = [
'sum' => '0',
];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$jrnlBudId = intval($transaction->transaction_journal_budget_id);
@@ -202,6 +204,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
$budgetId = max($jrnlBudId, $transBudId);
$result[$budgetId] = $result[$budgetId] ?? '0';
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
$result['sum'] = bcadd($result['sum'], $transaction->transaction_amount);
}
return $result;

View File

@@ -102,7 +102,12 @@ class Support
*/
protected function getObjectSummary(array $spent, array $earned): array
{
$return = [];
$return = [
'sum' => [
'spent' => '0',
'earned' => '0',
],
];
/**
* @var int
@@ -110,10 +115,11 @@ class Support
*/
foreach ($spent as $objectId => $entry) {
if (!isset($return[$objectId])) {
$return[$objectId] = ['spent' => 0, 'earned' => 0];
$return[$objectId] = ['spent' => '0', 'earned' => '0'];
}
$return[$objectId]['spent'] = $entry;
$return['sum']['spent'] = bcadd($return['sum']['spent'], $entry);
}
unset($entry);
@@ -123,10 +129,11 @@ class Support
*/
foreach ($earned as $objectId => $entry) {
if (!isset($return[$objectId])) {
$return[$objectId] = ['spent' => 0, 'earned' => 0];
$return[$objectId] = ['spent' => '0', 'earned' => '0'];
}
$return[$objectId]['earned'] = $entry;
$return['sum']['earned'] = bcadd($return['sum']['earned'], $entry);
}
return $return;
@@ -139,12 +146,15 @@ class Support
*/
protected function summarizeByAccount(Collection $collection): array
{
$result = [];
$result = [
'sum' => '0',
];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$accountId = $transaction->account_id;
$result[$accountId] = $result[$accountId] ?? '0';
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
$result['sum'] = bcadd($result['sum'], $transaction->transaction_amount);
}
return $result;

View File

@@ -25,11 +25,11 @@ namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Factories\RoleFactory;
use FireflyIII\Mail\ConfirmEmailChangeMail;
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
use FireflyIII\Mail\UndoEmailChangeMail;
use FireflyIII\Models\Role;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Auth\Events\Login;
@@ -74,11 +74,12 @@ class UserEventHandler
*/
public function checkSingleUserIsAdmin(Login $event): bool
{
Log::debug('In checkSingleUserIsAdmin');
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
/** @var User $user */
$user = $event->user;
$count = User::count();
$count = $repository->count();
if ($count > 1) {
// if more than one user, do nothing.
@@ -93,17 +94,16 @@ class UserEventHandler
return true;
}
// user is the only user but does not have role "owner".
$role = Role::where('name', 'owner')->first();
$role = $repository->getRole('owner');
if (is_null($role)) {
// create role, does not exist. Very strange situation so let's raise a big fuss about it.
$role = Role::create(['name' => 'owner', 'display_name' => 'Site Owner', 'description' => 'User runs this instance of FF3']);
$role = $repository->createRole('owner', 'Site Owner', 'User runs this instance of FF3');
Log::error('Could not find role "owner". This is weird.');
}
Log::info(sprintf('Gave user #%d role #%d ("%s")', $user->id, $role->id, $role->name));
// give user the role
$user->attachRole($role);
$user->save();
$repository->attachRole($user, 'owner');
return true;
}

View File

@@ -25,6 +25,9 @@ namespace FireflyIII\Handlers\Events;
use FireflyConfig;
use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Services\Github\Object\Release;
use FireflyIII\Services\Github\Request\UpdateRequest;
use FireflyIII\User;
use Log;
@@ -45,6 +48,7 @@ class VersionCheckEventHandler
return;
}
/** @var User $user */
$user = $event->user;
if (!$user->hasRole('owner')) {
@@ -55,7 +59,7 @@ class VersionCheckEventHandler
$lastCheckTime = FireflyConfig::get('last_update_check', time());
$now = time();
if ($now - $lastCheckTime->data < 604800) {
Log::debug('Checked for updates less than a week ago.');
Log::debug(sprintf('Checked for updates less than a week ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data)));
return;
@@ -71,8 +75,42 @@ class VersionCheckEventHandler
return;
}
// actually check for update and inform the user.
$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' => '']);
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()));
}
$string = 'no result: ' . $check;
if ($check === -2) {
$string = strval(trans('firefly.update_check_error'));
}
if ($check === -1) {
// there is a new FF version!
$monthAndDayFormat = (string)trans('config.month_and_day');
$string = strval(
trans(
'firefly.update_new_version_alert',
['your_version' => $current, 'new_version' => $first->getTitle(), 'date' => $first->getUpdated()->formatLocalized($monthAndDayFormat)]
)
);
}
if ($check !== 0) {
// flash info
session()->flash('info', $string);
}
return;
}
}

View File

@@ -37,6 +37,7 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
@@ -232,7 +233,7 @@ class JournalCollector implements JournalCollectorInterface
$countQuery->getQuery()->groups = null;
$countQuery->getQuery()->orders = null;
$countQuery->groupBy('accounts.user_id');
$this->count = $countQuery->count();
$this->count = intval($countQuery->count());
return $this->count;
}
@@ -243,6 +244,17 @@ class JournalCollector implements JournalCollectorInterface
public function getJournals(): Collection
{
$this->run = true;
// find query set in cache.
$hash = hash('sha256', $this->query->toSql() . serialize($this->query->getBindings()));
$key = 'query-' . substr($hash, -8);
$cache = new CacheProperties;
$cache->addProperty($key);
if ($cache->has()) {
Log::debug(sprintf('Return cache of query with ID "%s".', $key));
return $cache->get(); // @codeCoverageIgnore
}
/** @var Collection $set */
$set = $this->query->get(array_values($this->fields));
@@ -263,6 +275,8 @@ class JournalCollector implements JournalCollectorInterface
$transaction->opposing_account_iban = app('steam')->tryDecrypt($transaction->opposing_account_iban);
}
);
Log::debug(sprintf('Cached query with ID "%s".', $key));
$cache->store($set);
return $set;
}

View File

@@ -27,6 +27,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;

View File

@@ -136,7 +136,7 @@ class PopupReport implements PopupReportInterface
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setRange($attributes['startDate'], $attributes['endDate'])
->setRange($attributes['startDate'], $attributes['endDate'])->withOpposingAccount()
->setCategory($category);
$journals = $collector->getJournals();

View File

@@ -39,7 +39,6 @@ use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use Steam;
use View;
@@ -51,6 +50,13 @@ use View;
*/
class AccountController extends Controller
{
/** @var CurrencyRepositoryInterface */
private $currencyRepos;
/** @var JournalRepositoryInterface */
private $journalRepos;
/** @var AccountRepositoryInterface */
private $repository;
/**
*
*/
@@ -64,6 +70,10 @@ class AccountController extends Controller
app('view')->share('mainTitleIcon', 'fa-credit-card');
app('view')->share('title', trans('firefly.accounts'));
$this->repository = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->journalRepos = app(JournalRepositoryInterface::class);
return $next($request);
}
);
@@ -77,9 +87,7 @@ class AccountController extends Controller
*/
public function create(Request $request, string $what = 'asset')
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$allCurrencies = $repository->get();
$allCurrencies = $this->currencyRepos->get();
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
$defaultCurrency = app('amount')->getDefaultCurrency();
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
@@ -102,16 +110,15 @@ class AccountController extends Controller
}
/**
* @param AccountRepositoryInterface $repository
* @param Account $account
* @param Account $account
*
* @return View
*/
public function delete(AccountRepositoryInterface $repository, Account $account)
public function delete(Account $account)
{
$typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type);
$subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]);
$accountList = ExpandedForm::makeSelectListWithEmpty($repository->getAccountsByType([$account->accountType->type]));
$accountList = ExpandedForm::makeSelectListWithEmpty($this->repository->getAccountsByType([$account->accountType->type]));
unset($accountList[$account->id]);
// put previous url in session
@@ -127,14 +134,14 @@ class AccountController extends Controller
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, AccountRepositoryInterface $repository, Account $account)
public function destroy(Request $request, Account $account)
{
$type = $account->accountType->type;
$typeName = config('firefly.shortNamesByFullName.' . $type);
$name = $account->name;
$moveTo = $repository->find(intval($request->get('move_account_before_delete')));
$moveTo = $this->repository->find(intval($request->get('move_account_before_delete')));
$repository->destroy($account, $moveTo);
$this->repository->destroy($account, $moveTo);
$request->session()->flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name])));
Preferences::mark();
@@ -154,17 +161,13 @@ class AccountController extends Controller
* @return View
*
* @throws FireflyException
* @throws FireflyException
* @throws FireflyException
*/
public function edit(Request $request, Account $account, AccountRepositoryInterface $repository)
public function edit(Request $request, Account $account)
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$what = config('firefly.shortNamesByFullName')[$account->accountType->type];
$subTitle = trans('firefly.edit_' . $what . '_account', ['name' => $account->name]);
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$allCurrencies = $currencyRepos->get();
$allCurrencies = $this->currencyRepos->get();
$currencySelectList = ExpandedForm::makeSelectList($allCurrencies);
$roles = [];
foreach (config('firefly.accountRoles') as $role) {
@@ -184,7 +187,7 @@ class AccountController extends Controller
$openingBalanceAmount = '0' === $account->getOpeningBalanceAmount() ? '' : $openingBalanceAmount;
$openingBalanceDate = $account->getOpeningBalanceDate();
$openingBalanceDate = 1900 === $openingBalanceDate->year ? null : $openingBalanceDate->format('Y-m-d');
$currency = $currencyRepos->find(intval($account->getMeta('currency_id')));
$currency = $this->currencyRepos->find(intval($account->getMeta('currency_id')));
$preFilled = [
'accountNumber' => $account->getMeta('accountNumber'),
@@ -199,7 +202,7 @@ class AccountController extends Controller
'notes' => '',
];
/** @var Note $note */
$note = $repository->getNote($account);
$note = $this->repository->getNote($account);
if (null !== $note) {
$preFilled['notes'] = $note->text;
}
@@ -224,19 +227,18 @@ class AccountController extends Controller
}
/**
* @param Request $request
* @param AccountRepositoryInterface $repository
* @param string $what
* @param Request $request
* @param string $what
*
* @return View
*/
public function index(Request $request, AccountRepositoryInterface $repository, string $what)
public function index(Request $request, string $what)
{
$what = $what ?? 'asset';
$subTitle = trans('firefly.' . $what . '_accounts');
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $what);
$types = config('firefly.accountTypesByIdentifier.' . $what);
$collection = $repository->getAccountsByType($types);
$collection = $this->repository->getAccountsByType($types);
$total = $collection->count();
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
@@ -272,10 +274,9 @@ class AccountController extends Controller
/**
* Show an account.
*
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param Account $account
* @param string $moment
* @param Request $request
* @param Account $account
* @param string $moment
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*
@@ -284,23 +285,21 @@ class AccountController extends Controller
*
* @throws FireflyException
*/
public function show(Request $request, JournalRepositoryInterface $repository, Account $account, string $moment = '')
public function show(Request $request, Account $account, string $moment = '')
{
if (AccountType::INITIAL_BALANCE === $account->accountType->type) {
return $this->redirectToOriginalAccount($account);
}
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$range = Preferences::get('viewRange', '1M')->data;
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$chartUri = route('chart.account.single', [$account->id]);
$start = null;
$end = null;
$periods = new Collection;
$currencyId = intval($account->getMeta('currency_id'));
$currency = $currencyRepos->find($currencyId);
$range = Preferences::get('viewRange', '1M')->data;
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$chartUri = route('chart.account.single', [$account->id]);
$start = null;
$end = null;
$periods = new Collection;
$currencyId = intval($account->getMeta('currency_id'));
$currency = $this->currencyRepos->find($currencyId);
if (0 === $currencyId) {
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
}
@@ -309,7 +308,7 @@ class AccountController extends Controller
if ('all' === $moment) {
$subTitle = trans('firefly.all_journals_for_account', ['name' => $account->name]);
$chartUri = route('chart.account.all', [$account->id]);
$first = $repository->first();
$first = $this->journalRepos->first();
$start = $first->date ?? new Carbon;
$end = new Carbon;
}
@@ -317,12 +316,13 @@ class AccountController extends Controller
// prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment);
$start = app('navigation')->startOfPeriod($start, $range);
$end = app('navigation')->endOfPeriod($start, $range);
$fStart = $start->formatLocalized($this->monthAndDayFormat);
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]);
$periods = $this->getPeriodOverview($account);
$periods = $this->getPeriodOverview($account, $start);
}
// prep for current period view
@@ -332,7 +332,7 @@ class AccountController extends Controller
$fStart = $start->formatLocalized($this->monthAndDayFormat);
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
$periods = $this->getPeriodOverview($account);
$periods = $this->getPeriodOverview($account, null);
}
// grab journals:
@@ -351,15 +351,14 @@ class AccountController extends Controller
}
/**
* @param AccountFormRequest $request
* @param AccountRepositoryInterface $repository
* @param AccountFormRequest $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function store(AccountFormRequest $request, AccountRepositoryInterface $repository)
public function store(AccountFormRequest $request)
{
$data = $request->getAccountData();
$account = $repository->store($data);
$account = $this->repository->store($data);
$request->session()->flash('success', strval(trans('firefly.stored_new_account', ['name' => $account->name])));
Preferences::mark();
@@ -390,10 +389,10 @@ class AccountController extends Controller
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account)
public function update(AccountFormRequest $request, Account $account)
{
$data = $request->getAccountData();
$repository->update($account, $data);
$this->repository->update($account, $data);
$request->session()->flash('success', strval(trans('firefly.updated_account', ['name' => $account->name])));
Preferences::mark();
@@ -435,56 +434,55 @@ class AccountController extends Controller
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
private function getPeriodOverview(Account $account): Collection
private function getPeriodOverview(Account $account, ?Carbon $date): Collection
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$start = $repository->oldestJournalDate($account);
$range = Preferences::get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($start, $range);
$end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection;
$count = 0;
$range = Preferences::get('viewRange', '1M')->data;
$start = $this->repository->oldestJournalDate($account);
$end = $date ?? new Carbon;
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-show-period-entries');
$cache->addProperty($account->id);
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
Log::debug('Going to get period expenses and incomes.');
while ($end >= $start && $count < 90) {
$end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = app('navigation')->endOfPeriod($end, $range);
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = new Collection;
// loop dates
foreach ($dates as $date) {
// try a collector for income:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::DEPOSIT])->withOpposingAccount();
$collector->setAccounts(new Collection([$account]))->setRange($date['start'], $date['end'])->setTypes([TransactionType::DEPOSIT])
->withOpposingAccount();
$earned = strval($collector->getJournals()->sum('transaction_amount'));
// try a collector for expenses:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($end, $currentEnd)->setTypes([TransactionType::WITHDRAWAL])->withOpposingAccount();
$spent = strval($collector->getJournals()->sum('transaction_amount'));
$dateStr = $end->format('Y-m-d');
$dateName = app('navigation')->periodShow($end, $range);
$collector->setAccounts(new Collection([$account]))->setRange($date['start'], $date['end'])->setTypes([TransactionType::WITHDRAWAL])
->withOpposingAccount();
$spent = strval($collector->getJournals()->sum('transaction_amount'));
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['start'], $date['period']);
$entries->push(
[
'string' => $dateStr,
'name' => $dateName,
'spent' => $spent,
'earned' => $earned,
'date' => clone $end,]
'date' => clone $date['end'],]
);
$end = app('navigation')->subtractPeriod($end, $range, 1);
++$count;
}
$cache->store($entries);
return $entries;

View File

@@ -125,7 +125,7 @@ class UserController extends Controller
$list = ['twoFactorAuthEnabled', 'twoFactorAuthSecret'];
$preferences = Preferences::getArrayForUser($user, $list);
$user->isAdmin = $user->hasRole('owner');
$is2faEnabled = true === $preferences['twoFactorAuthEnabled'];
$is2faEnabled = 1 === $preferences['twoFactorAuthEnabled'];
$has2faSecret = null !== $preferences['twoFactorAuthSecret'];
$user->has2FA = ($is2faEnabled && $has2faSecret) ? true : false;
$user->prefs = $preferences;

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use File;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\AttachmentFormRequest;
use FireflyIII\Models\Attachment;
@@ -40,6 +39,9 @@ use View;
*/
class AttachmentController extends Controller
{
/** @var AttachmentRepositoryInterface */
private $repository;
/**
*
*/
@@ -52,6 +54,7 @@ class AttachmentController extends Controller
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-paperclip');
app('view')->share('title', trans('firefly.attachments'));
$this->repository = app(AttachmentRepositoryInterface::class);
return $next($request);
}
@@ -74,17 +77,16 @@ class AttachmentController extends Controller
}
/**
* @param Request $request
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
* @param Request $request
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
public function destroy(Request $request, Attachment $attachment)
{
$name = $attachment->filename;
$repository->destroy($attachment);
$this->repository->destroy($attachment);
$request->session()->flash('success', strval(trans('firefly.attachment_deleted', ['name' => $name])));
Preferences::mark();
@@ -93,17 +95,16 @@ class AttachmentController extends Controller
}
/**
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
* @param Attachment $attachment
*
* @return mixed
*
* @throws FireflyException
*/
public function download(AttachmentRepositoryInterface $repository, Attachment $attachment)
public function download(Attachment $attachment)
{
if ($repository->exists($attachment)) {
$content = $repository->getContent($attachment);
if ($this->repository->exists($attachment)) {
$content = $this->repository->getContent($attachment);
$quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\'));
/** @var LaravelResponse $response */
@@ -145,37 +146,15 @@ class AttachmentController extends Controller
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function preview(Attachment $attachment)
{
$image = 'images/page_green.png';
if ('application/pdf' === $attachment->mime) {
$image = 'images/page_white_acrobat.png';
}
$file = public_path($image);
$response = Response::make(File::get($file));
$response->header('Content-Type', 'image/png');
return $response;
}
/**
* @param AttachmentFormRequest $request
* @param AttachmentRepositoryInterface $repository
* @param Attachment $attachment
* @param AttachmentFormRequest $request
* @param Attachment $attachment
*
* @return \Illuminate\Http\RedirectResponse
*/
public function update(AttachmentFormRequest $request, AttachmentRepositoryInterface $repository, Attachment $attachment)
public function update(AttachmentFormRequest $request, Attachment $attachment)
{
$data = $request->getAttachmentData();
$repository->update($attachment, $data);
$this->repository->update($attachment, $data);
$request->session()->flash('success', strval(trans('firefly.attachment_updated', ['name' => $attachment->filename])));
Preferences::mark();
@@ -191,4 +170,25 @@ class AttachmentController extends Controller
// redirect to previous URL.
return redirect($this->getPreviousUri('attachments.edit.uri'));
}
/**
* @param Attachment $attachment
*
* @return \Illuminate\Http\Response
* @throws FireflyException
*/
public function view(Attachment $attachment)
{
if ($this->repository->exists($attachment)) {
$content = $this->repository->getContent($attachment);
return Response::make(
$content, 200, [
'Content-Type' => $attachment->mime,
'Content-Disposition' => 'inline; filename="' . $attachment->filename . '"',
]
);
}
throw new FireflyException('Could not find the indicated attachment. The file is no longer there.');
}
}

View File

@@ -262,7 +262,7 @@ class BillController extends Controller
}
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, $lastPaidDate);
$hideBill = true;
$subTitle = e($bill->name);
$subTitle = $bill->name;
return view('bills.show', compact('transactions', 'yearAverage', 'overallAverage', 'year', 'hideBill', 'bill', 'subTitle'));
}

View File

@@ -611,21 +611,19 @@ class BudgetController extends Controller
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
Log::debug('Going to get period expenses and incomes.');
while ($end >= $start) {
$end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = app('navigation')->endOfPeriod($end, $range);
$dates = app('navigation')->blockPeriods($start, $end, $range);
foreach ($dates as $date) {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutBudget()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutBudget()->withOpposingAccount()->setTypes(
[TransactionType::WITHDRAWAL]
);
$set = $collector->getJournals();
$sum = strval($set->sum('transaction_amount') ?? '0');
$journals = $set->count();
$dateStr = $end->format('Y-m-d');
$dateName = app('navigation')->periodShow($end, $range);
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $end]);
$end = app('navigation')->subtractPeriod($end, $range, 1);
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
$entries->push(['string' => $dateStr, 'name' => $dateName, 'count' => $journals, 'sum' => $sum, 'date' => clone $date['end']]);
}
$cache->store($entries);

View File

@@ -46,6 +46,13 @@ use View;
*/
class CategoryController extends Controller
{
/** @var AccountRepositoryInterface */
private $accountRepos;
/** @var JournalRepositoryInterface */
private $journalRepos;
/** @var CategoryRepositoryInterface */
private $repository;
/**
*
*/
@@ -57,6 +64,9 @@ class CategoryController extends Controller
function ($request, $next) {
app('view')->share('title', trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
$this->journalRepos = app(JournalRepositoryInterface::class);
$this->repository = app(CategoryRepositoryInterface::class);
$this->accountRepos = app(AccountRepositoryInterface::class);
return $next($request);
}
@@ -95,16 +105,15 @@ class CategoryController extends Controller
}
/**
* @param Request $request
* @param CategoryRepositoryInterface $repository
* @param Category $category
* @param Request $request
* @param Category $category
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, CategoryRepositoryInterface $repository, Category $category)
public function destroy(Request $request, Category $category)
{
$name = $category->name;
$repository->destroy($category);
$this->repository->destroy($category);
$request->session()->flash('success', strval(trans('firefly.deleted_category', ['name' => $name])));
Preferences::mark();
@@ -132,21 +141,21 @@ class CategoryController extends Controller
}
/**
* @param CategoryRepositoryInterface $repository
* @param Request $request
*
* @return View
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(Request $request, CategoryRepositoryInterface $repository)
public function index(Request $request)
{
$page = 0 === intval($request->get('page')) ? 1 : intval($request->get('page'));
$pageSize = intval(Preferences::get('listPageSize', 50)->data);
$collection = $repository->getCategories();
$collection = $this->repository->getCategories();
$total = $collection->count();
$collection = $collection->slice(($page - 1) * $pageSize, $pageSize);
$collection->each(
function (Category $category) use ($repository) {
$category->lastActivity = $repository->lastUseDate($category, new Collection);
function (Category $category) {
$category->lastActivity = $this->repository->lastUseDate($category, new Collection);
}
);
@@ -158,13 +167,12 @@ class CategoryController extends Controller
}
/**
* @param Request $request
* @param JournalRepositoryInterface $repository
* @param string $moment
* @param Request $request
* @param string $moment
*
* @return View
*/
public function noCategory(Request $request, JournalRepositoryInterface $repository, string $moment = '')
public function noCategory(Request $request, string $moment = '')
{
// default values:
$range = Preferences::get('viewRange', '1M')->data;
@@ -177,27 +185,27 @@ class CategoryController extends Controller
// prep for "all" view.
if ('all' === $moment) {
$subTitle = trans('firefly.all_journals_without_category');
$first = $repository->first();
$first = $this->journalRepos->first();
$start = $first->date ?? new Carbon;
$end = new Carbon;
}
// prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment);
$start = app('navigation')->startOfPeriod(new Carbon($moment), $range);
$end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
);
$periods = $this->getNoCategoryPeriodOverview();
$periods = $this->getNoCategoryPeriodOverview($start);
}
// prep for current period
if (0 === strlen($moment)) {
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getNoCategoryPeriodOverview();
$periods = $this->getNoCategoryPeriodOverview($start);
$subTitle = trans(
'firefly.without_category_between',
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
@@ -248,14 +256,14 @@ class CategoryController extends Controller
// prep for "specific date" view.
if (strlen($moment) > 0 && 'all' !== $moment) {
$start = new Carbon($moment);
$start = app('navigation')->startOfPeriod(new Carbon($moment), $range);
$end = app('navigation')->endOfPeriod($start, $range);
$subTitle = trans(
'firefly.journals_in_period_for_category',
['name' => $category->name,
'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),]
);
$periods = $this->getPeriodOverview($category);
$periods = $this->getPeriodOverview($category, $start);
$path = route('categories.show', [$category->id, $moment]);
}
@@ -265,7 +273,7 @@ class CategoryController extends Controller
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
/** @var Carbon $end */
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
$periods = $this->getPeriodOverview($category);
$periods = $this->getPeriodOverview($category, $start);
$subTitle = trans(
'firefly.journals_in_period_for_category',
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
@@ -336,38 +344,36 @@ class CategoryController extends Controller
}
/**
* @param Carbon $theDate
*
* @return Collection
*/
private function getNoCategoryPeriodOverview(): Collection
private function getNoCategoryPeriodOverview(Carbon $theDate): Collection
{
$repository = app(JournalRepositoryInterface::class);
$first = $repository->first();
$start = $first->date ?? new Carbon;
$range = Preferences::get('viewRange', '1M')->data;
$start = app('navigation')->startOfPeriod($start, $range);
$end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection;
$range = Preferences::get('viewRange', '1M')->data;
$first = $this->journalRepos->first();
$start = $first->date ?? new Carbon;
$end = $theDate ?? new Carbon;
// properties for cache
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('no-budget-period-entries');
$cache->addProperty('no-category-period-entries');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
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) {
Log::debug('Loop!');
$end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = app('navigation')->endOfPeriod($end, $range);
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = new Collection;
foreach ($dates as $date) {
// count journals without category in this period:
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$collector->removeFilter(InternalTransferFilter::class);
$count = $collector->getJournals()->count();
@@ -375,7 +381,7 @@ class CategoryController extends Controller
// amount transferred
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
$collector->removeFilter(InternalTransferFilter::class);
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
@@ -383,17 +389,20 @@ class CategoryController extends Controller
// amount spent
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::WITHDRAWAL]);
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
[TransactionType::WITHDRAWAL]
);
$spent = $collector->getJournals()->sum('transaction_amount');
// amount earned
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->withoutCategory()->withOpposingAccount()->setTypes([TransactionType::DEPOSIT]);
$earned = $collector->getJournals()->sum('transaction_amount');
$dateStr = $end->format('Y-m-d');
$dateName = app('navigation')->periodShow($end, $range);
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->withoutCategory()->withOpposingAccount()->setTypes(
[TransactionType::DEPOSIT]
);
$earned = $collector->getJournals()->sum('transaction_amount');
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
$entries->push(
[
'string' => $dateStr,
@@ -402,10 +411,9 @@ class CategoryController extends Controller
'spent' => $spent,
'earned' => $earned,
'transferred' => $transferred,
'date' => clone $end,
'date' => clone $date['end'],
]
);
$end = app('navigation')->subtractPeriod($end, $range, 1);
}
Log::debug('End of loops');
$cache->store($entries);
@@ -418,45 +426,39 @@ class CategoryController extends Controller
*
* @return Collection
*/
private function getPeriodOverview(Category $category): Collection
private function getPeriodOverview(Category $category, Carbon $date): Collection
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$first = $repository->firstUseDate($category);
if (null === $first) {
$first = new Carbon; // @codeCoverageIgnore
}
$range = Preferences::get('viewRange', '1M')->data;
$first = app('navigation')->startOfPeriod($first, $range);
$end = app('navigation')->endOfX(new Carbon, $range, null);
$entries = new Collection;
$count = 0;
$range = Preferences::get('viewRange', '1M')->data;
$first = $this->journalRepos->first();
$start = $first->date ?? new Carbon;
$end = $date ?? new Carbon;
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
// properties for entries with their amounts.
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($range);
$cache->addProperty('categories.entries');
$cache->addProperty($category->id);
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
while ($end >= $first && $count < 90) {
$end = app('navigation')->startOfPeriod($end, $range);
$currentEnd = app('navigation')->endOfPeriod($end, $range);
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd);
$dateStr = $end->format('Y-m-d');
$dateName = app('navigation')->periodShow($end, $range);
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = new Collection;
foreach ($dates as $date) {
$spent = $this->repository->spentInPeriod(new Collection([$category]), $accounts, $date['start'], $date['end']);
$earned = $this->repository->earnedInPeriod(new Collection([$category]), $accounts, $date['start'], $date['end']);
$dateStr = $date['end']->format('Y-m-d');
$dateName = app('navigation')->periodShow($date['end'], $date['period']);
// amount transferred
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAllAssetAccounts()->setRange($end, $currentEnd)->setCategory($category)
$collector->setAllAssetAccounts()->setRange($date['start'], $date['end'])->setCategory($category)
->withOpposingAccount()->setTypes([TransactionType::TRANSFER]);
$collector->removeFilter(InternalTransferFilter::class);
$transferred = Steam::positive($collector->getJournals()->sum('transaction_amount'));
@@ -469,11 +471,9 @@ class CategoryController extends Controller
'earned' => $earned,
'sum' => bcadd($earned, $spent),
'transferred' => $transferred,
'date' => clone $end,
'date' => clone $date['end'],
]
);
$end = app('navigation')->subtractPeriod($end, $range, 1);
++$count;
}
$cache->store($entries);

View File

@@ -76,21 +76,46 @@ class AccountController extends Controller
$repository = app(AccountRepositoryInterface::class);
$start = $repository->oldestJournalDate($account);
$end = new Carbon;
$format = (string)trans('config.month_and_day');
$range = Steam::balanceInRange($account, $start, $end);
$current = clone $start;
$previous = array_values($range)[0];
$chartData = [];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$label = $current->formatLocalized($format);
$chartData[$label] = $balance;
$previous = $balance;
$current->addDay();
// depending on diff, do something with range of chart.
$step = '1D';
$months = $start->diffInMonths($end);
if ($months > 3) {
$step = '1W';
}
if ($months > 24) {
$step = '1M'; // @codeCoverageIgnore
}
if ($months > 100) {
$step = '1Y'; // @codeCoverageIgnore
}
$chartData = [];
$current = clone $start;
switch ($step) {
case '1D':
$format = (string)trans('config.month_and_day');
$range = Steam::balanceInRange($account, $start, $end);
$previous = array_values($range)[0];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous;
$label = $current->formatLocalized($format);
$chartData[$label] = floatval($balance);
$previous = $balance;
$current->addDay();
}
break;
case '1W':
case '1M': // @codeCoverageIgnore
case '1Y': // @codeCoverageIgnore
while ($end >= $current) {
$balance = floatval(Steam::balance($account, $current));
$label = app('navigation')->periodShow($current, $step);
$chartData[$label] = $balance;
$current = app('navigation')->addPeriod($current, $step, 1);
}
break;
}
$data = $this->generator->singleSet($account->name, $chartData);
$cache->store($data);

View File

@@ -37,7 +37,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
use Preferences;
use Response;
use Steam;
@@ -78,37 +77,47 @@ class BudgetController extends Controller
*/
public function budget(Budget $budget)
{
$first = $this->repository->firstUseDate($budget);
$range = Preferences::get('viewRange', '1M')->data;
$currentStart = app('navigation')->startOfPeriod($first, $range);
$last = session('end', new Carbon);
$cache = new CacheProperties();
$cache->addProperty($first);
$cache->addProperty($last);
$start = $this->repository->firstUseDate($budget);
$end = session('end', new Carbon);
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('chart.budget.budget');
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$final = clone $last;
$final->addYears(2);
// depending on diff, do something with range of chart.
$step = '1D';
$months = $start->diffInMonths($end);
if ($months > 3) {
$step = '1W';
}
if ($months > 24) {
$step = '1M';
}
if ($months > 60) {
$step = '1Y'; // @codeCoverageIgnore
}
$budgetCollection = new Collection([$budget]);
$last = app('navigation')->endOfX($last, $range, $final); // not to overshoot.
$entries = [];
while ($currentStart < $last) {
// periodspecific dates:
$currentEnd = app('navigation')->endOfPeriod($currentStart, $range);
// sub another day because reasons.
$currentEnd->subDay();
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd);
$format = app('navigation')->periodShow($currentStart, $range);
$entries[$format] = bcmul($spent, '-1');
$currentStart = clone $currentEnd;
$currentStart->addDays(2);
$chartData = [];
$current = clone $start;
$current = app('navigation')->startOfPeriod($current, $step);
while ($end >= $current) {
$currentEnd = app('navigation')->endOfPeriod($current, $step);
if ($step === '1Y') {
$currentEnd->subDay(); // @codeCoverageIgnore
}
$spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $current, $currentEnd);
$label = app('navigation')->periodShow($current, $step);
$chartData[$label] = floatval(bcmul($spent, '-1'));
$current = clone $currentEnd;
$current->addDay();
}
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $entries);
$data = $this->generator->singleSet(strval(trans('firefly.spent')), $chartData);
$cache->store($data);

View File

@@ -25,6 +25,8 @@ namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use DB;
use Exception;
use FireflyIII\Http\Middleware\IsDemoUser;
use Illuminate\Http\Request;
use Log;
use Monolog\Handler\RotatingFileHandler;
@@ -34,6 +36,15 @@ use Monolog\Handler\RotatingFileHandler;
*/
class DebugController extends Controller
{
/**
* HomeController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(IsDemoUser::class);
}
/**
* @param Request $request
@@ -74,7 +85,11 @@ class DebugController extends Controller
if ($handler instanceof RotatingFileHandler) {
$logFile = $handler->getUrl();
if (null !== $logFile) {
$logContent = file_get_contents($logFile);
try {
$logContent = file_get_contents($logFile);
} catch (Exception $e) {
// don't care
}
}
}
}

View File

@@ -200,20 +200,25 @@ class HomeController extends Controller
'login', 'logout', 'password.reset', 'profile.confirm-email-change', 'profile.undo-email-change',
'register', 'report.options', 'routes', 'rule-groups.down', 'rule-groups.up', 'rules.up', 'rules.down',
'rules.select', 'search.search', 'test-flash', 'transactions.link.delete', 'transactions.link.switch',
'two-factor.lost', 'report.options',
'two-factor.lost', 'reports.options', 'debug', 'import.create-job', 'import.download', 'import.start', 'import.status.json',
'preferences.delete-code', 'rules.test-triggers', 'piggy-banks.remove-money', 'piggy-banks.add-money',
'accounts.reconcile.transactions', 'accounts.reconcile.overview', 'export.download',
'transactions.clone', 'two-factor.index',
];
$return = '&nbsp;';
/** @var Route $route */
foreach ($set as $route) {
$name = $route->getName();
if (null !== $name && in_array('GET', $route->methods()) && strlen($name) > 0) {
$found = false;
foreach ($ignore as $string) {
if (false !== strpos($name, $string)) {
if (!(false === stripos($name, $string))) {
$found = true;
break;
}
}
if (!$found) {
if ($found === false) {
$return .= 'touch ' . $route->getName() . '.md;';
}
}

View File

@@ -55,7 +55,7 @@ class ConfigurationController extends Controller
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['index']);
$this->middleware(IsDemoUser::class);
}
/**

View File

@@ -33,6 +33,7 @@ use Log;
use Response;
use View;
/**
* Class FileController.
*/
@@ -57,8 +58,7 @@ class IndexController extends Controller
return $next($request);
}
);
$this->middleware(IsDemoUser::class)->except(['create', 'index']);
$this->middleware(IsDemoUser::class)->except(['index']);
}
/**

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Response;
@@ -47,6 +48,7 @@ class StatusController extends Controller
return $next($request);
}
);
$this->middleware(IsDemoUser::class);
}
/**
@@ -93,20 +95,29 @@ class StatusController extends Controller
$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]);
$result['finished'] = true;
$tagId = intval($job->extended_status['tag']);
if ($tagId !== 0) {
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tag = $repository->find($tagId);
$count = $tag->transactionJournals()->count();
$result['finishedText'] = trans(
'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]
);
}
if ($tagId === 0) {
$result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore
}
}
if ('running' === $job->status) {
$result['started'] = true;
$result['running'] = true;
}
$result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage'];
return Response::json($result);
}

View File

@@ -25,11 +25,13 @@ namespace FireflyIII\Http\Controllers\Json;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Response;
@@ -170,7 +172,7 @@ class BoxController extends Controller
*
* @return \Illuminate\Http\JsonResponse
*/
public function netWorth(AccountRepositoryInterface $repository)
public function netWorth(AccountRepositoryInterface $repository, CurrencyRepositoryInterface $currencyRepos)
{
$date = new Carbon(date('Y-m-d')); // needed so its per day.
/** @var Carbon $start */
@@ -193,16 +195,32 @@ class BoxController extends Controller
if ($cache->has()) {
return Response::json($cache->get()); // @codeCoverageIgnore
}
$netWorth = [];
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$currency = app('amount')->getDefaultCurrency();
$balances = app('steam')->balancesByAccounts($accounts, $date);
$sum = '0';
foreach ($balances as $entry) {
$sum = bcadd($sum, $entry);
/** @var Account $account */
foreach ($accounts as $account) {
$accountCurrency = $currency;
$balance = $balances[$account->id] ?? '0';
$currencyId = intval($account->getMeta('currency_id'));
if ($currencyId !== 0) {
$accountCurrency = $currencyRepos->find($currencyId);
}
if (!isset($netWorth[$accountCurrency->id])) {
$netWorth[$accountCurrency->id]['currency'] = $accountCurrency;
$netWorth[$accountCurrency->id]['sum'] = '0';
}
$netWorth[$accountCurrency->id]['sum'] = bcadd($netWorth[$accountCurrency->id]['sum'], $balance);
}
$return = [];
foreach ($netWorth as $currencyId => $data) {
$return[$currencyId] = app('amount')->formatAnything($data['currency'], $data['sum'], false);
}
$return = [
'net_worth' => app('amount')->formatAnything($currency, $sum, false),
'net_worths' => array_values($return),
];
$cache->store($return);

View File

@@ -383,7 +383,7 @@ class PiggyBankController extends Controller
{
$note = $piggyBank->notes()->first();
$events = $repository->getEvents($piggyBank);
$subTitle = e($piggyBank->name);
$subTitle = $piggyBank->name;
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'note'));
}

View File

@@ -59,16 +59,16 @@ class ReportController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
// @var AccountRepositoryInterface $repository
/** @var AccountRepositoryInterface accountRepository */
$this->accountRepository = app(AccountRepositoryInterface::class);
// @var BudgetRepositoryInterface $repository
/** @var BudgetRepositoryInterface budgetRepository */
$this->budgetRepository = app(BudgetRepositoryInterface::class);
// @var CategoryRepositoryInterface categoryRepository
/** @var CategoryRepositoryInterface categoryRepository */
$this->categoryRepository = app(CategoryRepositoryInterface::class);
// @var PopupReportInterface popupHelper
/** @var PopupReportInterface popupHelper */
$this->popupHelper = app(PopupReportInterface::class);
return $next($request);

View File

@@ -128,7 +128,7 @@ class PreferencesController extends Controller
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation.
*/
public function postCode(TokenFormRequest $request)
public function postCode(/** @scrutinizer ignore-unused */ TokenFormRequest $request)
{
Preferences::set('twoFactorAuthEnabled', 1);
Preferences::set('twoFactorAuthSecret', Session::get('two-factor-secret'));

View File

@@ -129,12 +129,12 @@ class CategoryController extends Controller
$report = [];
/** @var Category $category */
foreach ($categories as $category) {
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
if (0 !== bccomp($spent, '0')) {
$report[$category->id] = ['name' => $category->name, 'spent' => $spent, 'id' => $category->id];
$spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end);
$earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $end);
if (0 !== bccomp($spent, '0') || 0 !== bccomp($earned, '0')) {
$report[$category->id] = ['name' => $category->name, 'spent' => $spent, 'earned' => $earned, 'id' => $category->id];
}
}
// sort the result
// Obtain a list of columns
$sum = [];

View File

@@ -568,7 +568,7 @@ class ExpenseController extends Controller
];
// loop to support multi currency
foreach ($set as $transaction) {
$currencyId = $transaction->transaction_currency_id;
$currencyId = intval($transaction->transaction_currency_id);
// if not set, set to zero:
if (!isset($sum['per_currency'][$currencyId])) {

View File

@@ -48,6 +48,9 @@ class ReportController extends Controller
/** @var ReportHelperInterface */
protected $helper;
/** @var BudgetRepositoryInterface */
private $repository;
/**
*
*/
@@ -55,13 +58,13 @@ class ReportController extends Controller
{
parent::__construct();
$this->helper = app(ReportHelperInterface::class);
$this->middleware(
function ($request, $next) {
app('view')->share('title', trans('firefly.reports'));
app('view')->share('mainTitleIcon', 'fa-line-chart');
View::share('subTitleIcon', 'fa-calendar');
$this->helper = app(ReportHelperInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);
}
@@ -87,6 +90,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle', trans(
@@ -120,6 +124,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle',
@@ -157,6 +162,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle',
@@ -195,6 +201,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle',
@@ -233,6 +240,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle',
@@ -265,6 +273,7 @@ class ReportController extends Controller
$customFiscalYear = Preferences::get('customFiscalYear', 0)->data;
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$accountList = join(',', $accounts->pluck('id')->toArray());
$this->repository->cleanupBudgets();
return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear'));
}
@@ -391,6 +400,7 @@ class ReportController extends Controller
if ($start < session('first')) {
$start = session('first');
}
$this->repository->cleanupBudgets();
View::share(
'subTitle',

View File

@@ -38,6 +38,11 @@ use URL;
*/
class LinkController extends Controller
{
/** @var JournalRepositoryInterface */
private $journalRepository;
/** @var LinkTypeRepositoryInterface */
private $repository;
/**
*
*/
@@ -50,6 +55,9 @@ class LinkController extends Controller
app('view')->share('title', trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
$this->journalRepository = app(JournalRepositoryInterface::class);
$this->repository = app(LinkTypeRepositoryInterface::class);
return $next($request);
}
);
@@ -70,14 +78,13 @@ class LinkController extends Controller
}
/**
* @param LinkTypeRepositoryInterface $repository
* @param TransactionJournalLink $link
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
public function destroy(TransactionJournalLink $link)
{
$repository->destroyLink($link);
$this->repository->destroyLink($link);
Session::flash('success', strval(trans('firefly.deleted_link')));
Preferences::mark();
@@ -87,18 +94,13 @@ class LinkController extends Controller
/**
* @param JournalLinkRequest $request
* @param LinkTypeRepositoryInterface $repository
* @param JournalRepositoryInterface $journalRepository
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function store(
JournalLinkRequest $request,
LinkTypeRepositoryInterface $repository,
JournalRepositoryInterface $journalRepository,
TransactionJournal $journal
) {
public function store(JournalLinkRequest $request, TransactionJournal $journal)
{
Log::debug('We are here (store)');
$linkInfo = $request->getLinkInfo();
if (0 === $linkInfo['transaction_journal_id']) {
@@ -106,30 +108,28 @@ class LinkController extends Controller
return redirect(route('transactions.show', [$journal->id]));
}
$other = $journalRepository->find($linkInfo['transaction_journal_id']);
$alreadyLinked = $repository->findLink($journal, $other);
$other = $this->journalRepository->find($linkInfo['transaction_journal_id']);
$alreadyLinked = $this->repository->findLink($journal, $other);
if ($alreadyLinked) {
Session::flash('error', trans('firefly.journals_error_linked'));
return redirect(route('transactions.show', [$journal->id]));
}
Log::debug(sprintf('Journal is %d, opposing is %d', $journal->id, $other->id));
$repository->storeLink($linkInfo, $other, $journal);
$this->repository->storeLink($linkInfo, $other, $journal);
Session::flash('success', trans('firefly.journals_linked'));
return redirect(route('transactions.show', [$journal->id]));
}
/**
* @param LinkTypeRepositoryInterface $repository
* @param TransactionJournalLink $link
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function switchLink(LinkTypeRepositoryInterface $repository, TransactionJournalLink $link)
public function switchLink(TransactionJournalLink $link)
{
$repository->switchLink($link);
$this->repository->switchLink($link);
return redirect(URL::previous());
}

View File

@@ -272,7 +272,7 @@ class TransactionController extends Controller
$return = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$currencyId = $transaction->transaction_currency_id;
$currencyId = intval($transaction->transaction_currency_id);
// save currency information:
if (!isset($return[$currencyId])) {

View File

@@ -24,11 +24,10 @@ namespace FireflyIII\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Log;
use Preferences;
use Auth;
use Session;
/**
* Class AuthenticateTwoFactor.
*/
@@ -45,26 +44,14 @@ class AuthenticateTwoFactor
*/
public function handle(Request $request, Closure $next, $guard = null)
{
// do the usual auth, again:
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('login');
}
if (1 === intval(auth()->user()->blocked)) {
Auth::guard($guard)->logout();
Session::flash('logoutMessage', trans('firefly.block_account_logout'));
return redirect()->guest('login');
}
$is2faEnabled = Preferences::get('twoFactorAuthEnabled', false)->data;
$has2faSecret = null !== Preferences::get('twoFactorAuthSecret');
// grab 2auth information from session.
$is2faAuthed = 'true' === $request->cookie('twoFactorAuthenticated');
$is2faAuthed = 'true' === $request->cookie('twoFactorAuthenticated');
if ($is2faEnabled && $has2faSecret && !$is2faAuthed) {
Log::debug('Does not seem to be 2 factor authed, redirect.');

View File

@@ -51,9 +51,9 @@ class IsDemoUser
/** @var User $user */
$user = auth()->user();
if ($user->hasRole('demo')) {
Session::flash('warning', strval(trans('firefly.not_available_demo_user')));
Session::flash('info', strval(trans('firefly.not_available_demo_user')));
return redirect(route('index'));
return redirect($request->session()->previousUrl());
}
return $next($request);

View File

@@ -60,8 +60,8 @@ class TrustProxies extends Middleware
public function __construct(Repository $config)
{
$trustedProxies = env('TRUSTED_PROXIES', null);
if (null !== $trustedProxies && strlen($trustedProxies) > 0) {
$this->proxies = $trustedProxies;
if (false !== $trustedProxies && null !== $trustedProxies && strlen($trustedProxies) > 0) {
$this->proxies = strval($trustedProxies);
}
parent::__construct($config);

View File

@@ -48,7 +48,7 @@ class JournalLinkRequest extends Request
$parts = explode('_', $linkType);
$return['link_type_id'] = intval($parts[0]);
$return['transaction_journal_id'] = $this->integer('link_journal_id');
$return['comments'] = strlen($this->string('comments')) > 0 ? $this->string('comments') : null;
$return['notes'] = strlen($this->string('notes')) > 0 ? $this->string('notes') : '';
$return['direction'] = $parts[1];
if (0 === $return['transaction_journal_id'] && ctype_digit($this->string('link_other'))) {
$return['transaction_journal_id'] = $this->integer('link_other');

View File

@@ -88,11 +88,9 @@ class FileConfigurator implements ConfiguratorInterface
if (is_null($this->job)) {
throw new FireflyException('Cannot call configureJob() without a job.');
}
$class = $this->getConfigurationClass();
$job = $this->job;
/** @var ConfigurationInterface $object */
$object = app($class);
$object->setJob($job);
$object = app($this->getConfigurationClass());
$object->setJob($this->job);
$result = $object->storeConfiguration($data);
$this->warning = $object->getWarningMessage();
@@ -111,12 +109,9 @@ class FileConfigurator implements ConfiguratorInterface
if (is_null($this->job)) {
throw new FireflyException('Cannot call getNextData() without a job.');
}
$class = $this->getConfigurationClass();
$job = $this->job;
/** @var ConfigurationInterface $object */
$object = app($class);
$object->setJob($job);
$object = app($this->getConfigurationClass());
$object->setJob($this->job);
return $object->getData();
}
@@ -192,6 +187,12 @@ class FileConfigurator implements ConfiguratorInterface
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($job->user);
// set number of steps to 100:
$extendedStatus = $this->getExtendedStatus();
$extendedStatus['steps'] = 6;
$extendedStatus['done'] = 0;
$this->setExtendedStatus($extendedStatus);
$config = $this->getConfig();
$newConfig = array_merge($this->defaultConfig, $config);
$this->repository->setConfiguration($job, $newConfig);
@@ -246,4 +247,28 @@ class FileConfigurator implements ConfiguratorInterface
return $class;
}
/**
* Shorthand method to return the extended status.
*
* @codeCoverageIgnore
* @return array
*/
private function getExtendedStatus(): array
{
return $this->repository->getExtendedStatus($this->job);
}
/**
* Shorthand method to set the extended status.
*
* @codeCoverageIgnore
* @param array $extended
*/
private function setExtendedStatus(array $extended): void
{
$this->repository->setExtendedStatus($this->job, $extended);
return;
}
}

View File

@@ -24,6 +24,7 @@ namespace FireflyIII\Import\Configuration;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Import\Configuration\Spectre\HaveAccounts;
use Log;
@@ -35,6 +36,9 @@ class SpectreConfigurator implements ConfiguratorInterface
/** @var ImportJob */
private $job;
/** @var ImportJobRepositoryInterface */
private $repository;
/** @var string */
private $warning = '';
@@ -51,12 +55,14 @@ class SpectreConfigurator implements ConfiguratorInterface
* @param array $data
*
* @return bool
* @throws FireflyException
*/
public function configureJob(array $data): bool
{
$config = $this->job->configuration;
$stage = $config['stage'];
$status = $this->job->status;
if (is_null($this->job)) {
throw new FireflyException('Cannot call configureJob() without a job.');
}
$stage = $this->getConfig()['stage'] ?? 'initial';
Log::debug(sprintf('in getNextData(), for stage "%s".', $stage));
switch ($stage) {
case 'have-accounts':
@@ -66,13 +72,11 @@ class SpectreConfigurator implements ConfiguratorInterface
$class->storeConfiguration($data);
// update job for next step and set to "configured".
$config = $this->job->configuration;
$config['stage'] = 'have-account-mapping';
$this->job->configuration = $config;
$this->job->status = 'configured';
$this->job->save();
$config = $this->getConfig();
$config['stage'] = 'have-account-mapping';
$this->repository->setConfiguration($this->job, $config);
return true;
break;
default:
throw new FireflyException(sprintf('Cannot store configuration when job is in state "%s"', $stage));
break;
@@ -83,12 +87,16 @@ class SpectreConfigurator implements ConfiguratorInterface
* Return the data required for the next step in the job configuration.
*
* @return array
* @throws FireflyException
*/
public function getNextData(): array
{
$config = $this->job->configuration;
$stage = $config['stage'];
$status = $this->job->status;
if (is_null($this->job)) {
throw new FireflyException('Cannot call configureJob() without a job.');
}
$config = $this->getConfig();
$stage = $config['stage'] ?? 'initial';
Log::debug(sprintf('in getNextData(), for stage "%s".', $stage));
switch ($stage) {
case 'has-token':
@@ -96,9 +104,13 @@ class SpectreConfigurator implements ConfiguratorInterface
$config['is-redirected'] = true;
$config['stage'] = 'user-logged-in';
$status = 'configured';
break;
// update config and status:
$this->repository->setConfiguration($this->job, $config);
$this->repository->setStatus($this->job, $status);
return $this->repository->getConfiguration($this->job);
case 'have-accounts':
// use special class:
/** @var HaveAccounts $class */
$class = app(HaveAccounts::class);
$class->setJob($this->job);
@@ -107,37 +119,30 @@ class SpectreConfigurator implements ConfiguratorInterface
return $data;
default:
return [];
break;
}
// update config and status:
$this->job->configuration = $config;
$this->job->status = $status;
$this->job->save();
return $this->job->configuration;
}
/**
* @return string
* @throws FireflyException
*/
public function getNextView(): string
{
$config = $this->job->configuration;
$stage = $config['stage'];
if (is_null($this->job)) {
throw new FireflyException('Cannot call configureJob() without a job.');
}
$stage = $this->getConfig()['stage'] ?? 'initial';
Log::debug(sprintf('in getNextView(), for stage "%s".', $stage));
switch ($stage) {
case 'has-token':
// redirect to Spectre.
Log::info('User is being redirected to Spectre.');
return 'import.spectre.redirect';
break;
case 'have-accounts':
return 'import.spectre.accounts';
break;
default:
return '';
break;
}
}
@@ -154,11 +159,14 @@ class SpectreConfigurator implements ConfiguratorInterface
/**
* @return bool
* @throws FireflyException
*/
public function isJobConfigured(): bool
{
$config = $this->job->configuration;
$stage = $config['stage'];
if (is_null($this->job)) {
throw new FireflyException('Cannot call configureJob() without a job.');
}
$stage = $this->getConfig()['stage'] ?? 'initial';
Log::debug(sprintf('in isJobConfigured(), for stage "%s".', $stage));
switch ($stage) {
case 'has-token':
@@ -176,9 +184,14 @@ class SpectreConfigurator implements ConfiguratorInterface
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job)
public function setJob(ImportJob $job): void
{
$defaultConfig = [
// make repository
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($job->user);
// set default config:
$defaultConfig = [
'has-token' => false,
'token' => '',
'token-expires' => 0,
@@ -190,16 +203,31 @@ class SpectreConfigurator implements ConfiguratorInterface
'accounts' => '',
'accounts-mapped' => '',
'auto-start' => true,
'apply-rules' => true,
'match-bills' => false,
];
$extendedStatus = $job->extended_status;
$extendedStatus['steps'] = 100;
$currentConfig = $this->repository->getConfiguration($job);
$finalConfig = array_merge($defaultConfig, $currentConfig);
// set default extended status:
$extendedStatus = $this->repository->getExtendedStatus($job);
$extendedStatus['steps'] = 6;
$config = $job->configuration;
$finalConfig = array_merge($defaultConfig, $config);
$job->configuration = $finalConfig;
$job->extended_status = $extendedStatus;
$job->save();
// save to job:
$job = $this->repository->setConfiguration($job, $finalConfig);
$job = $this->repository->setExtendedStatus($job, $extendedStatus);
$this->job = $job;
return;
}
/**
* Shorthand method.
*
* @return array
*/
private function getConfig(): array
{
return $this->repository->getConfiguration($this->job);
}
}

View File

@@ -77,25 +77,25 @@ class Amount implements ConverterInterface
Log::debug(sprintf('Searched from the left for "." in amount "%s", assume this is the decimal sign.', $value));
$decimal = '.';
}
unset($options, $res);
unset($res);
}
// if decimal is dot, replace all comma's and spaces with nothing. then parse as float (round to 4 pos)
if ('.' === $decimal) {
$search = [',', ' '];
$value = str_replace($search, '', $value);
$search = [',', ' '];
$value = str_replace($search, '', $value);
Log::debug(sprintf('Converted amount from "%s" to "%s".', $original, $value));
}
if (',' === $decimal) {
$search = ['.', ' '];
$value = str_replace($search, '', $value);
$value = str_replace(',', '.', $value);
$search = ['.', ' '];
$value = str_replace($search, '', $value);
$value = str_replace(',', '.', $value);
Log::debug(sprintf('Converted amount from "%s" to "%s".', $original, $value));
}
if (null === $decimal) {
// replace all:
$search = ['.', ' ', ','];
$value = str_replace($search, '', $value);
$search = ['.', ' ', ','];
$value = str_replace($search, '', $value);
Log::debug(sprintf('No decimal character found. Converted amount from "%s" to "%s".', $original, $value));
}

View File

@@ -90,6 +90,7 @@ class CsvProcessor implements FileProcessorInterface
Log::debug('Now in CsvProcessor run(). Job is now running...');
$entries = new Collection($this->getImportArray());
$this->addStep();
Log::notice('Building importable objects from CSV file.');
Log::debug(sprintf('Number of entries: %d', $entries->count()));
$notImported = $entries->filter(
@@ -97,8 +98,7 @@ class CsvProcessor implements FileProcessorInterface
$row = array_values($row);
if ($this->rowAlreadyImported($row)) {
$message = sprintf('Row #%d has already been imported.', $index);
$this->repository->addStepsDone($this->job, 5);
$this->addError($index, $message);
$this->repository->addError($this->job, $index, $message);
Log::info($message);
return null;
@@ -107,30 +107,24 @@ class CsvProcessor implements FileProcessorInterface
return $row;
}
);
$this->addStep();
Log::debug(sprintf('Number of entries left: %d', $notImported->count()));
// set (new) number of steps:
$extended = $this->getExtendedStatus();
$steps = $notImported->count() * 5;
$extended['steps'] = $steps;
$this->setExtendedStatus($extended);
Log::debug(sprintf('Number of steps: %d', $steps));
$notImported->each(
function (array $row, int $index) {
$journal = $this->importRow($index, $row);
$this->objects->push($journal);
$this->repository->addStepsDone($this->job, 1);
}
);
$this->addStep();
return true;
}
/**
* @codeCoverageIgnore
* Shorthand method
* Shorthand method to set the extended status.
*
* @codeCoverageIgnore
* @param array $array
*/
public function setExtendedStatus(array $array)
@@ -155,20 +149,13 @@ class CsvProcessor implements FileProcessorInterface
}
/**
* Shorthand method.
* Shorthand method to add a step.
*
* @codeCoverageIgnore
*
* @param int $index
* @param string $message
*/
private function addError(int $index, string $message): void
private function addStep()
{
$extended = $this->getExtendedStatus();
$extended['errors'][$index][] = $message;
$this->setExtendedStatus($extended);
return;
$this->repository->addStepsDone($this->job, 1);
}
/**
@@ -202,9 +189,9 @@ class CsvProcessor implements FileProcessorInterface
}
/**
* @codeCoverageIgnore
* Shorthand method.
* Shorthand method to return configuration.
*
* @codeCoverageIgnore
* @return array
*/
private function getConfig(): array
@@ -212,24 +199,11 @@ class CsvProcessor implements FileProcessorInterface
return $this->repository->getConfiguration($this->job);
}
/**
* @codeCoverageIgnore
* Shorthand method.
*
* @return array
*/
private function getExtendedStatus(): array
{
return $this->repository->getExtendedStatus($this->job);
}
/**
* @return Iterator
*
* @throws \League\Csv\Exception
* @throws \League\Csv\Exception
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function getImportArray(): Iterator
{
@@ -375,7 +349,7 @@ class CsvProcessor implements FileProcessorInterface
*/
private function specifics(array $row): array
{
$config = $this->job->configuration;
$config = $this->getConfig();
$names = array_keys($config['specifics'] ?? []);
foreach ($names as $name) {
if (!in_array($name, $this->validSpecifics)) {

View File

@@ -48,6 +48,8 @@ class ImportJournal
public $currency;
/** @var string */
public $description = '';
/** @var ImportCurrency */
public $foreignCurrency;
/** @var string */
public $hash;
/** @var array */
@@ -71,6 +73,8 @@ class ImportJournal
/** @var string */
private $externalId = '';
/** @var array */
private $foreignAmount;
/** @var array */
private $modifiers = [];
/** @var User */
private $user;
@@ -80,12 +84,13 @@ class ImportJournal
*/
public function __construct()
{
$this->asset = new ImportAccount;
$this->opposing = new ImportAccount;
$this->bill = new ImportBill;
$this->category = new ImportCategory;
$this->budget = new ImportBudget;
$this->currency = new ImportCurrency;
$this->asset = new ImportAccount;
$this->opposing = new ImportAccount;
$this->bill = new ImportBill;
$this->category = new ImportCategory;
$this->budget = new ImportBudget;
$this->currency = new ImportCurrency;
$this->foreignCurrency = new ImportCurrency;
}
/**
@@ -179,6 +184,8 @@ class ImportJournal
*/
public function setValue(array $array)
{
$array['mapped'] = $array['mapped'] ?? null;
$array['value'] = $array['value'] ?? null;
switch ($array['role']) {
default:
throw new FireflyException(sprintf('ImportJournal cannot handle "%s" with value "%s".', $array['role'], $array['value']));
@@ -188,6 +195,12 @@ class ImportJournal
case 'amount':
$this->amount = $array;
break;
case 'amount_foreign':
$this->foreignAmount = $array;
break;
case 'foreign-currency-code':
$this->foreignCurrency->setId($array);
break;
case 'amount_debit':
$this->amountDebit = $array;
break;

View File

@@ -22,6 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Import\Prerequisites;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\User;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
@@ -62,9 +63,13 @@ class FilePrerequisites implements PrerequisitesInterface
* True if prerequisites. False if not.
*
* @return bool
* @throws FireflyException
*/
public function hasPrerequisites(): bool
{
if($this->user->hasRole('demo')) {
throw new FireflyException('Apologies, the demo user cannot import files.');
}
return false;
}

View File

@@ -28,6 +28,7 @@ use FireflyIII\Import\FileProcessor\FileProcessorInterface;
use FireflyIII\Import\Storage\ImportStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
@@ -46,6 +47,9 @@ class FileRoutine implements RoutineInterface
/** @var ImportJob */
private $job;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* ImportRoutine constructor.
*/
@@ -84,26 +88,32 @@ class FileRoutine implements RoutineInterface
*/
public function run(): bool
{
if ('configured' !== $this->job->status) {
Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->job->status));
if ('configured' !== $this->getStatus()) {
Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->getStatus()));
return false;
}
set_time_limit(0);
Log::info(sprintf('Start with import job %s', $this->job->key));
// total steps: 6
$this->setTotalSteps(6);
$importObjects = $this->getImportObjects();
$this->lines = $importObjects->count();
$this->addStep();
// total steps can now be extended. File has been scanned. 7 steps per line:
$this->addTotalSteps(7 * $this->lines);
// once done, use storage thing to actually store them:
Log::info(sprintf('Returned %d valid objects from file processor', $this->lines));
$storage = $this->storeObjects($importObjects);
$this->addStep();
Log::debug('Back in run()');
// update job:
$this->job->status = 'finished';
$this->job->save();
Log::debug('Updated job...');
Log::debug(sprintf('%d journals in $storage->journals', $storage->journals->count()));
@@ -114,6 +124,10 @@ class FileRoutine implements RoutineInterface
// create tag, link tag to all journals:
$this->createImportTag();
$this->addStep();
// update job:
$this->setStatus('finished');
Log::info(sprintf('Done with import job %s', $this->job->key));
@@ -125,7 +139,9 @@ class FileRoutine implements RoutineInterface
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
$this->job = $job;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($job->user);
}
/**
@@ -134,18 +150,16 @@ class FileRoutine implements RoutineInterface
protected function getImportObjects(): Collection
{
$objects = new Collection;
$config = $this->job->configuration;
$fileType = $config['file-type'] ?? 'csv';
$fileType = $this->getConfig()['file-type'] ?? 'csv';
// will only respond to "file"
$class = config(sprintf('import.options.file.processors.%s', $fileType));
/** @var FileProcessorInterface $processor */
$processor = app($class);
$processor->setJob($this->job);
if ('configured' === $this->job->status) {
if ('configured' === $this->getStatus()) {
// set job as "running"...
$this->job->status = 'running';
$this->job->save();
$this->setStatus('running');
Log::debug('Job is configured, start with run()');
$processor->run();
@@ -155,6 +169,14 @@ class FileRoutine implements RoutineInterface
return $objects;
}
/**
* Shorthand method.
*/
private function addStep()
{
$this->repository->addStepsDone($this->job, 1);
}
/**
*
*/
@@ -167,11 +189,12 @@ class FileRoutine implements RoutineInterface
return new Tag;
}
$this->addTotalSteps($this->journals->count() + 2);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->job->user);
$data = [
$data = [
'tag' => trans('import.import_with_key', ['key' => $this->job->key]),
'date' => new Carbon,
'description' => null,
@@ -180,11 +203,11 @@ class FileRoutine implements RoutineInterface
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
$extended = $this->job->extended_status;
$extended['tag'] = $tag->id;
$this->job->extended_status = $extended;
$this->job->save();
$tag = $repository->store($data);
$this->addStep();
$extended = $this->getExtendedStatus();
$extended['tag'] = $tag->id;
$this->setExtendedStatus($extended);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
@@ -193,12 +216,81 @@ class FileRoutine implements RoutineInterface
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
$this->addStep();
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag));
$this->addStep();
return $tag;
}
/**
* Shorthand method
*
* @return array
*/
private function getConfig(): array
{
return $this->repository->getConfiguration($this->job);
}
/**
* @return array
*/
private function getExtendedStatus(): array
{
return $this->repository->getExtendedStatus($this->job);
}
/**
* Shorthand method.
*
* @return string
*/
private function getStatus(): string
{
return $this->repository->getStatus($this->job);
}
/**
* @param array $extended
*/
private function setExtendedStatus(array $extended): void
{
$this->repository->setExtendedStatus($this->job, $extended);
return;
}
/**
* Shorthand
*
* @param string $status
*/
private function setStatus(string $status): void
{
$this->repository->setStatus($this->job, $status);
}
/**
* Shorthand
*
* @param int $steps
*/
private function setTotalSteps(int $steps)
{
$this->repository->setTotalSteps($this->job, $steps);
}
/**
* Shorthand
*
* @param int $steps
*/
private function addTotalSteps(int $steps)
{
$this->repository->addTotalSteps($this->job, $steps);
}
/**
* @param Collection $objects
*
@@ -206,9 +298,10 @@ class FileRoutine implements RoutineInterface
*/
private function storeObjects(Collection $objects): ImportStorage
{
$config = $this->getConfig();
$storage = new ImportStorage;
$storage->setJob($this->job);
$storage->setDateFormat($this->job->configuration['date-format']);
$storage->setDateFormat($config['date-format']);
$storage->setObjects($objects);
$storage->store();
Log::info('Back in storeObjects()');

View File

@@ -22,15 +22,21 @@ declare(strict_types=1);
namespace FireflyIII\Import\Routine;
use Carbon\Carbon;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Object\ImportJournal;
use FireflyIII\Import\Storage\ImportStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Services\Spectre\Exception\DuplicatedCustomerException;
use FireflyIII\Services\Spectre\Exception\SpectreException;
use FireflyIII\Services\Spectre\Object\Account;
use FireflyIII\Services\Spectre\Object\Customer;
use FireflyIII\Services\Spectre\Object\Login;
use FireflyIII\Services\Spectre\Object\Token;
use FireflyIII\Services\Spectre\Object\Transaction;
use FireflyIII\Services\Spectre\Request\CreateTokenRequest;
use FireflyIII\Services\Spectre\Request\ListAccountsRequest;
use FireflyIII\Services\Spectre\Request\ListCustomersRequest;
@@ -116,15 +122,14 @@ class SpectreRoutine implements RoutineInterface
*/
public function run(): bool
{
if ('configured' === $this->job->status) {
if ('configured' === $this->getStatus()) {
$this->repository->updateStatus($this->job, 'running');
}
Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key));
set_time_limit(0);
// check if job has token first!
$config = $this->job->configuration;
$stage = $config['stage'];
$stage = $this->getConfig()['stage'] ?? 'unknown';
switch ($stage) {
case 'initial':
@@ -144,10 +149,7 @@ class SpectreRoutine implements RoutineInterface
throw new FireflyException(sprintf('Cannot handle stage %s', $stage));
}
var_dump($config);
exit;
throw new FireflyException('Application cannot handle this.');
return true;
}
/**
@@ -189,6 +191,7 @@ class SpectreRoutine implements RoutineInterface
}
}
Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray());
return $customer;
@@ -202,21 +205,20 @@ class SpectreRoutine implements RoutineInterface
*/
protected function getCustomer(): Customer
{
$config = $this->job->configuration;
$config = $this->getConfig();
if (!is_null($config['customer'])) {
$customer = new Customer($config['customer']);
return $customer;
}
$customer = $this->createCustomer();
$config['customer'] = [
$customer = $this->createCustomer();
$config['customer'] = [
'id' => $customer->getId(),
'identifier' => $customer->getIdentifier(),
'secret' => $customer->getSecret(),
];
$this->job->configuration = $config;
$this->job->save();
$this->setConfig($config);
return $customer;
}
@@ -259,20 +261,20 @@ class SpectreRoutine implements RoutineInterface
Log::debug(sprintf('Token is %s', $token->getToken()));
// update job, give it the token:
$config = $this->job->configuration;
$config['has-token'] = true;
$config['token'] = $token->getToken();
$config['token-expires'] = $token->getExpiresAt()->format('U');
$config['token-url'] = $token->getConnectUrl();
$config['stage'] = 'has-token';
$this->job->configuration = $config;
$config = $this->getConfig();
$config['has-token'] = true;
$config['token'] = $token->getToken();
$config['token-expires'] = $token->getExpiresAt()->format('U');
$config['token-url'] = $token->getConnectUrl();
$config['stage'] = 'has-token';
$this->setConfig($config);
Log::debug('Job config is now', $config);
// update job, set status to "configuring".
$this->job->status = 'configuring';
$this->job->save();
$this->setStatus('configuring');
Log::debug(sprintf('Job status is now %s', $this->job->status));
$this->addStep();
}
/**
@@ -287,6 +289,7 @@ class SpectreRoutine implements RoutineInterface
$request = new ListLoginsRequest($this->job->user);
$request->setCustomer($customer);
$request->call();
$logins = $request->getLogins();
/** @var Login $final */
$final = null;
@@ -302,8 +305,13 @@ class SpectreRoutine implements RoutineInterface
}
}
if (is_null($final)) {
throw new FireflyException('No valid login attempt found.');
Log::error('Could not find a valid login for this user.');
$this->repository->addError($this->job, 0, 'Spectre connection failed. Did you use invalid credentials, press Cancel or failed the 2FA challenge?');
$this->repository->setStatus($this->job, 'error');
return;
}
$this->addStep();
// list the users accounts using this login.
$accountRequest = new ListAccountsRequest($this->job->user);
@@ -319,45 +327,266 @@ class SpectreRoutine implements RoutineInterface
}
// update job:
$config = $this->job->configuration;
$config['accounts'] = $all;
$config['login'] = $login->toArray();
$config['stage'] = 'have-accounts';
$this->job->configuration = $config;
$this->job->status = 'configuring';
$this->job->save();
$config = $this->getConfig();
$config['accounts'] = $all;
$config['login'] = $login->toArray();
$config['stage'] = 'have-accounts';
$this->setConfig($config);
$this->setStatus('configuring');
$this->addStep();
return;
}
/**
* Shorthand method.
*/
private function addStep()
{
$this->repository->addStepsDone($this->job, 1);
}
/**
* Shorthand
*
* @param int $steps
*/
private function addTotalSteps(int $steps)
{
$this->repository->addTotalSteps($this->job, $steps);
}
/**
* @return array
*/
private function getConfig(): array
{
return $this->repository->getConfiguration($this->job);
}
/**
* Shorthand method.
*
* @return array
*/
private function getExtendedStatus(): array
{
return $this->repository->getExtendedStatus($this->job);
}
/**
* Shorthand method.
*
* @return string
*/
private function getStatus(): string
{
return $this->repository->getStatus($this->job);
}
/**
* @param array $all
*
* @throws FireflyException
*/
private function importTransactions(array $all)
{
Log::debug('Going to import transactions');
$collection = new Collection;
// create import objects?
foreach ($all as $accountId => $data) {
Log::debug(sprintf('Now at account #%d', $accountId));
/** @var Transaction $transaction */
foreach ($data['transactions'] as $transaction) {
Log::debug(sprintf('Now at transaction #%d', $transaction->getId()));
/** @var Account $account */
$account = $data['account'];
$importJournal = new ImportJournal;
$importJournal->setUser($this->job->user);
$importJournal->asset->setDefaultAccountId($data['import_id']);
// call set value a bunch of times for various data entries:
$tags = [];
$tags[] = $transaction->getMode();
$tags[] = $transaction->getStatus();
if ($transaction->isDuplicated()) {
$tags[] = 'possibly-duplicated';
}
$extra = $transaction->getExtra()->toArray();
$notes = '';
$notes .= strval(trans('import.imported_from_account', ['account' => $account->getName()])) . ' '
. "\n"; // double space for newline in Markdown.
foreach ($extra as $key => $value) {
switch ($key) {
case 'account_number':
$importJournal->setValue(['role' => 'account-number', 'value' => $value]);
break;
case 'original_category':
case 'original_subcategory':
case 'customer_category_code':
case 'customer_category_name':
$tags[] = $value;
break;
case 'payee':
$importJournal->setValue(['role' => 'opposing-name', 'value' => $value]);
break;
case 'original_amount':
$importJournal->setValue(['role' => 'amount_foreign', 'value' => $value]);
break;
case 'original_currency_code':
$importJournal->setValue(['role' => 'foreign-currency-code', 'value' => $value]);
break;
default:
$notes .= $key . ': ' . $value . ' '; // for newline in Markdown.
}
}
// hash
$importJournal->setHash($transaction->getHash());
// account ID (Firefly III account):
$importJournal->setValue(['role' => 'account-id', 'value' => $data['import_id'], 'mapped' => $data['import_id']]);
// description:
$importJournal->setValue(['role' => 'description', 'value' => $transaction->getDescription()]);
// date:
$importJournal->setValue(['role' => 'date-transaction', 'value' => $transaction->getMadeOn()->toIso8601String()]);
// amount
$importJournal->setValue(['role' => 'amount', 'value' => $transaction->getAmount()]);
$importJournal->setValue(['role' => 'currency-code', 'value' => $transaction->getCurrencyCode()]);
// various meta fields:
$importJournal->setValue(['role' => 'category-name', 'value' => $transaction->getCategory()]);
$importJournal->setValue(['role' => 'note', 'value' => $notes]);
$importJournal->setValue(['role' => 'tags-comma', 'value' => join(',', $tags)]);
$collection->push($importJournal);
}
}
$this->addStep();
Log::debug(sprintf('Going to try and store all %d them.', $collection->count()));
$this->addTotalSteps(7 * $collection->count());
// try to store them (seven steps per transaction)
$storage = new ImportStorage;
$storage->setJob($this->job);
$storage->setDateFormat('Y-m-d\TH:i:sO');
$storage->setObjects($collection);
$storage->store();
Log::info('Back in importTransactions()');
// link to tag
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->job->user);
$data = [
'tag' => trans('import.import_with_key', ['key' => $this->job->key]),
'date' => new Carbon,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
$extended = $this->getExtendedStatus();
$extended['tag'] = $tag->id;
$this->setExtendedStatus($extended);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $storage->journals->pluck('id')->toArray();
$tagId = $tag->id;
$this->addTotalSteps(count($journalIds));
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
$this->addStep();
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $storage->journals->count(), $tag->id, $tag->tag));
// set status to "finished"?
// update job:
$this->setStatus('finished');
$this->addStep();
return;
}
/**
* @throws FireflyException
* @throws SpectreException
*/
private function runStageHaveMapping()
{
// for each spectre account id in 'account-mappings'.
// find FF account
// get transactions.
// import?!
$config = $this->job->configuration;
$config = $this->getConfig();
$accounts = $config['accounts'] ?? [];
$all = [];
$count = 0;
/** @var array $accountArray */
foreach ($accounts as $accountArray) {
$account = new Account($accountArray);
$importId = intval($config['accounts-mapped'][$account->getid()] ?? 0);
$doImport = $importId !== 0 ? true : false;
if (!$doImport) {
Log::debug(sprintf('Will NOT import from Spectre account #%d ("%s")', $account->getId(), $account->getName()));
continue;
}
// import into account
// grab all transactions
$listTransactionsRequest = new ListTransactionsRequest($this->job->user);
$listTransactionsRequest->setAccount($account);
$listTransactionsRequest->call();
$transactions = $listTransactionsRequest->getTransactions();
var_dump($transactions);exit;
$transactions = $listTransactionsRequest->getTransactions();
$all[$account->getId()] = [
'account' => $account,
'import_id' => $importId,
'transactions' => $transactions,
];
$count += count($transactions);
}
var_dump($config);
exit;
Log::debug(sprintf('Total number of transactions: %d', $count));
$this->addStep();
$this->importTransactions($all);
}
/**
* Shorthand.
*
* @param array $config
*/
private function setConfig(array $config): void
{
$this->repository->setConfiguration($this->job, $config);
return;
}
/**
* Shorthand method.
*
* @param array $extended
*/
private function setExtendedStatus(array $extended): void
{
$this->repository->setExtendedStatus($this->job, $extended);
return;
}
/**
* Shorthand.
*
* @param string $status
*/
private function setStatus(string $status): void
{
$this->repository->setStatus($this->job, $status);
}
}

View File

@@ -30,11 +30,22 @@ use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Preferences;
/**
* Is capable of storing individual ImportJournal objects.
* Adds 7 steps per object stored:
* 1. get all import data from import journal
* 2. is not a duplicate
* 3. create the journal
* 4. store journal
* 5. run rules
* 6. run bills
* 7. finished storing object
*
* Class ImportStorage.
*/
class ImportStorage
@@ -53,6 +64,8 @@ class ImportStorage
protected $defaultCurrencyId = 1;
/** @var ImportJob */
protected $job;
/** @var ImportJobRepositoryInterface */
protected $repository;
/** @var Collection */
protected $rules;
/** @var bool */
@@ -63,6 +76,8 @@ class ImportStorage
private $matchBills = false;
/** @var Collection */
private $objects;
/** @var int */
private $total = 0;
/** @var array */
private $transfers = [];
@@ -89,13 +104,17 @@ class ImportStorage
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
$currency = app('amount')->getDefaultCurrencyByUser($this->job->user);
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($job->user);
$config = $this->repository->getConfiguration($job);
$currency = app('amount')->getDefaultCurrencyByUser($job->user);
$this->defaultCurrencyId = $currency->id;
$this->job = $job;
$this->transfers = $this->getTransfers();
$config = $job->configuration;
$this->applyRules = $config['apply-rules'] ?? false;
$this->matchBills = $config['match-bills'] ?? false;
if (true === $this->applyRules) {
Log::debug('applyRules seems to be true, get the rules.');
$this->rules = $this->getRules();
@@ -108,6 +127,8 @@ class ImportStorage
}
Log::debug(sprintf('Value of apply rules is %s', var_export($this->applyRules, true)));
Log::debug(sprintf('Value of match bills is %s', var_export($this->matchBills, true)));
}
/**
@@ -116,6 +137,7 @@ class ImportStorage
public function setObjects(Collection $objects)
{
$this->objects = $objects;
$this->total = $objects->count();
}
/**
@@ -129,6 +151,7 @@ class ImportStorage
function (ImportJournal $importJournal, int $index) {
try {
$this->storeImportJournal($index, $importJournal);
$this->addStep();
} catch (FireflyException | ErrorException | Exception $e) {
$this->errors->push($e->getMessage());
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
@@ -150,7 +173,7 @@ class ImportStorage
*/
protected function storeImportJournal(int $index, ImportJournal $importJournal): bool
{
Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->getDescription()));
Log::debug(sprintf('Going to store object #%d/%d with description "%s"', ($index + 1), $this->total, $importJournal->getDescription()));
$assetAccount = $importJournal->asset->getAccount();
$amount = $importJournal->getAmount();
$currencyId = $this->getCurrencyId($importJournal);
@@ -159,9 +182,7 @@ class ImportStorage
$opposingAccount = $this->getOpposingAccount($importJournal->opposing, $assetAccount->id, $amount);
$transactionType = $this->getTransactionType($amount, $opposingAccount);
$description = $importJournal->getDescription();
// First step done!
$this->job->addStepsDone(1);
$this->addStep();
/**
* Check for double transfer.
@@ -175,13 +196,17 @@ class ImportStorage
'opposing' => $opposingAccount->name,
];
if ($this->isDoubleTransfer($parameters) || $this->hashAlreadyImported($importJournal->hash)) {
$this->job->addStepsDone(3);
// throw error
$message = sprintf('Detected a possible duplicate, skip this one (hash: %s).', $importJournal->hash);
Log::error($message, $parameters);
// add five steps to keep the pace:
$this->addSteps(5);
throw new FireflyException($message);
}
unset($parameters);
$this->addStep();
// store journal and create transactions:
$parameters = [
@@ -197,9 +222,7 @@ class ImportStorage
];
$journal = $this->storeJournal($parameters);
unset($parameters);
// Another step done!
$this->job->addStepsDone(1);
$this->addStep();
// store meta object things:
$this->storeCategory($journal, $importJournal->category->getCategory());
@@ -221,31 +244,31 @@ class ImportStorage
// set journal completed:
$journal->completed = true;
$journal->save();
// Another step done!
$this->job->addStepsDone(1);
$this->addStep();
// run rules if config calls for it:
if (true === $this->applyRules) {
Log::info('Will apply rules to this journal.');
$this->applyRules($journal);
}
Preferences::setForUser($this->job->user, 'lastActivity', microtime());
if (!(true === $this->applyRules)) {
Log::info('Will NOT apply rules to this journal.');
}
$this->addStep();
// match bills if config calls for it.
if (true === $this->matchBills) {
Log::info('Cannot match bills (yet).');
Log::info('Will match bills.');
$this->matchBills($journal);
}
if (!(true === $this->matchBills)) {
Log::info('Cannot match bills (yet), but do not have to.');
}
$this->addStep();
// Another step done!
$this->job->addStepsDone(1);
$this->journals->push($journal);
Log::info(sprintf('Imported new journal #%d: "%s", amount %s %s.', $journal->id, $journal->description, $journal->transactionCurrency->code, $amount));
@@ -253,6 +276,24 @@ class ImportStorage
return true;
}
/**
* Shorthand method.
*/
private function addStep()
{
$this->repository->addStepsDone($this->job, 1);
}
/**
* Shorthand method
*
* @param int $steps
*/
private function addSteps(int $steps)
{
$this->repository->addStepsDone($this->job, $steps);
}
/**
* @param array $parameters
*
@@ -269,7 +310,6 @@ class ImportStorage
$amount = app('steam')->positive($parameters['amount']);
$names = [$parameters['asset'], $parameters['opposing']];
$transfer = [];
sort($names);

View File

@@ -121,11 +121,11 @@ trait ImportSupport
{
$transaction = new Transaction;
$transaction->account_id = $parameters['account'];
$transaction->transaction_journal_id = $parameters['id'];
$transaction->transaction_currency_id = $parameters['currency'];
$transaction->transaction_journal_id = intval($parameters['id']);
$transaction->transaction_currency_id = intval($parameters['currency']);
$transaction->amount = $parameters['amount'];
$transaction->foreign_currency_id = $parameters['foreign_currency'];
$transaction->foreign_amount = $parameters['foreign_amount'];
$transaction->foreign_currency_id = intval($parameters['foreign_currency']) === 0 ? null : intval($parameters['foreign_currency']);
$transaction->foreign_amount = null === $transaction->foreign_currency_id ? null : $parameters['foreign_amount'];
$transaction->save();
if (null === $transaction->id) {
$errorText = join(', ', $transaction->getErrors()->all());
@@ -155,6 +155,7 @@ trait ImportSupport
* @param ImportJournal $importJournal
*
* @return int
* @throws FireflyException
*/
private function getCurrencyId(ImportJournal $importJournal): int
{
@@ -192,7 +193,7 @@ trait ImportSupport
{
// use given currency by import journal.
$currency = $importJournal->currency->getTransactionCurrency();
if (null !== $currency->id && $currency->id !== $currencyId) {
if (null !== $currency->id && intval($currency->id) !== intval($currencyId)) {
return $currency->id;
}
@@ -216,6 +217,7 @@ trait ImportSupport
* @see ImportSupport::getTransactionType
*
* @return Account
* @throws FireflyException
*/
private function getOpposingAccount(ImportAccount $account, int $forbiddenAccount, string $amount): Account
{
@@ -435,8 +437,6 @@ trait ImportSupport
if (!$journal->save()) {
$errorText = join(', ', $journal->getErrors()->all());
// add three steps:
$this->job->addStepsDone(3);
// throw error
throw new FireflyException($errorText);
}

View File

@@ -212,7 +212,7 @@ class Account extends Model
*
* @return string
*/
public function getNameAttribute($value): string
public function getNameAttribute($value): ?string
{
if ($this->encrypted) {
return Crypt::decrypt($value);

View File

@@ -30,6 +30,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* Class ExportJob.
*
* @property User $user
* @property string $key
*/
class ExportJob extends Model
{

View File

@@ -82,32 +82,6 @@ class ImportJob extends Model
throw new NotFoundHttpException;
}
/**
* @param int $index
* @param string $message
*
* @return bool
*/
public function addError(int $index, string $message): bool
{
$extended = $this->extended_status;
$extended['errors'][$index][] = $message;
$this->extended_status = $extended;
return true;
}
/**
* @param int $count
*/
public function addStepsDone(int $count)
{
$status = $this->extended_status;
$status['done'] += $count;
$this->extended_status = $status;
$this->save();
}
/**
* @param int $count
*/
@@ -117,6 +91,7 @@ class ImportJob extends Model
$status['steps'] += $count;
$this->extended_status = $status;
$this->save();
Log::debug(sprintf('Add %d to total steps for job "%s" making total steps %d', $count, $this->key, $status['steps']));
}
/**

View File

@@ -158,7 +158,7 @@ class PiggyBank extends Model
public function leftOnAccount(Carbon $date): string
{
$balance = Steam::balanceIgnoreVirtual($this->account, $date);
// @var PiggyBank $p
/** @var PiggyBank $piggyBank */
foreach ($this->account->piggyBanks as $piggyBank) {
$currentAmount = $piggyBank->currentRelevantRep()->currentamount ?? '0';

View File

@@ -66,6 +66,7 @@ use Watson\Validating\ValidatingTrait;
* @property string $transaction_currency_symbol
* @property int $transaction_currency_dp
* @property string $transaction_currency_code
* @property string $description
*/
class Transaction extends Model
{

View File

@@ -28,6 +28,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class TransactionCurrency.
*
* @property string $code
*
*/
class TransactionCurrency extends Model
{

View File

@@ -92,8 +92,6 @@ class TransactionJournal extends Model
if (auth()->check()) {
$journalId = intval($value);
$journal = auth()->user()->transactionJournals()->where('transaction_journals.id', $journalId)
->with('transactionType')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->first(['transaction_journals.*']);
if (!is_null($journal)) {
return $journal;

View File

@@ -95,6 +95,15 @@ class TransactionJournalLink extends Model
return $this->belongsTo(LinkType::class);
}
/**
* @codeCoverageIgnore
* Get all of the notes.
*/
public function notes()
{
return $this->morphMany(Note::class, 'noteable');
}
/**
* @codeCoverageIgnore
*

View File

@@ -699,6 +699,7 @@ class AccountRepository implements AccountRepositoryInterface
return null;
}
return $iban;
}
}

View File

@@ -67,8 +67,11 @@ class BudgetRepository implements BudgetRepositoryInterface
if ($entry->ct > 1) {
$newest = BudgetLimit::where('start_date', $entry->start_date)->where('end_date', $entry->end_date)
->where('budget_id', $entry->budget_id)->orderBy('updated_at', 'DESC')->first(['budget_limits.*']);
BudgetLimit::where('start_date', $entry->start_date)->where('end_date', $entry->end_date)
->where('budget_id', $entry->budget_id)->where('id', '!=', $newest->id)->delete();
if (!is_null($newest)) {
BudgetLimit::where('start_date', $entry->start_date)->where('end_date', $entry->end_date)
->where('budget_id', $entry->budget_id)
->where('id', '!=', $newest->id)->delete();
}
}
}

View File

@@ -42,6 +42,21 @@ class ImportJobRepository implements ImportJobRepositoryInterface
/** @var User */
private $user;
/**
* @param ImportJob $job
* @param int $index
* @param string $error
*
* @return ImportJob
*/
public function addError(ImportJob $job, int $index, string $error): ImportJob
{
$extended = $this->getExtendedStatus($job);
$extended['errors'][$index][] = $error;
return $this->setExtendedStatus($job, $extended);
}
/**
* @param ImportJob $job
* @param int $steps
@@ -50,9 +65,28 @@ class ImportJobRepository implements ImportJobRepositoryInterface
*/
public function addStepsDone(ImportJob $job, int $steps = 1): ImportJob
{
$job->addStepsDone($steps);
$status = $this->getExtendedStatus($job);
$status['done'] += $steps;
Log::debug(sprintf('Add %d to steps done for job "%s" making steps done %d', $steps, $job->key, $status['done']));
return $this->setExtendedStatus($job, $status);
}
/**
* @param ImportJob $job
* @param int $steps
*
* @return ImportJob
*/
public function addTotalSteps(ImportJob $job, int $steps = 1): ImportJob
{
$extended = $this->getExtendedStatus($job);
$total = $extended['steps'] ?? 0;
$total += $steps;
$extended['steps'] = $total;
return $this->setExtendedStatus($job, $extended);
return $job;
}
/**
@@ -161,6 +195,16 @@ class ImportJobRepository implements ImportJobRepositoryInterface
return [];
}
/**
* @param ImportJob $job
*
* @return string
*/
public function getStatus(ImportJob $job): string
{
return $job->status;
}
/**
* @param ImportJob $job
* @param UploadedFile $file
@@ -277,6 +321,50 @@ class ImportJobRepository implements ImportJobRepositoryInterface
return $job;
}
/**
* @param ImportJob $job
* @param string $status
*
* @return ImportJob
*/
public function setStatus(ImportJob $job, string $status): ImportJob
{
$job->status = $status;
$job->save();
return $job;
}
/**
* @param ImportJob $job
* @param int $steps
*
* @return ImportJob
*/
public function setStepsDone(ImportJob $job, int $steps): ImportJob
{
$status = $this->getExtendedStatus($job);
$status['done'] = $steps;
Log::debug(sprintf('Set steps done for job "%s" to %d', $job->key, $steps));
return $this->setExtendedStatus($job, $status);
}
/**
* @param ImportJob $job
* @param int $count
*
* @return ImportJob
*/
public function setTotalSteps(ImportJob $job, int $count): ImportJob
{
$status = $this->getExtendedStatus($job);
$status['steps'] = $count;
Log::debug(sprintf('Set total steps for job "%s" to %d', $job->key, $count));
return $this->setExtendedStatus($job, $status);
}
/**
* @param User $user
*/

View File

@@ -31,6 +31,16 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
interface ImportJobRepositoryInterface
{
/**
* @param ImportJob $job
* @param int $index
* @param string $error
*
* @return ImportJob
*/
public function addError(ImportJob $job, int $index, string $error): ImportJob;
/**
* @param ImportJob $job
* @param int $steps
@@ -39,6 +49,14 @@ interface ImportJobRepositoryInterface
*/
public function addStepsDone(ImportJob $job, int $steps = 1): ImportJob;
/**
* @param ImportJob $job
* @param int $steps
*
* @return ImportJob
*/
public function addTotalSteps(ImportJob $job, int $steps = 1): ImportJob;
/**
* Return number of imported rows with this hash value.
*
@@ -80,6 +98,13 @@ interface ImportJobRepositoryInterface
*/
public function getExtendedStatus(ImportJob $job): array;
/**
* @param ImportJob $job
*
* @return string
*/
public function getStatus(ImportJob $job);
/**
* @param ImportJob $job
* @param UploadedFile $file
@@ -112,6 +137,30 @@ interface ImportJobRepositoryInterface
*/
public function setExtendedStatus(ImportJob $job, array $array): ImportJob;
/**
* @param ImportJob $job
* @param string $status
*
* @return ImportJob
*/
public function setStatus(ImportJob $job, string $status): ImportJob;
/**
* @param ImportJob $job
* @param int $count
*
* @return ImportJob
*/
public function setStepsDone(ImportJob $job, int $steps): ImportJob;
/**
* @param ImportJob $job
* @param int $count
*
* @return ImportJob
*/
public function setTotalSteps(ImportJob $job, int $count): ImportJob;
/**
* @param User $user
*/

View File

@@ -27,7 +27,6 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\SingleCacheProperties;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
@@ -70,14 +69,6 @@ class JournalTasker implements JournalTaskerInterface
*/
public function getTransactionsOverview(TransactionJournal $journal): array
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-overview');
$cache->addProperty($journal->id);
$cache->addProperty($journal->updated_at);
if ($cache->has()) {
return $cache->get();
}
// get all transaction data + the opposite site in one list.
$set = $journal
->transactions()// "source"
->leftJoin(
@@ -177,7 +168,6 @@ class JournalTasker implements JournalTaskerInterface
$transactions[] = $transaction;
}
$cache->store($transactions);
return $transactions;
}

View File

@@ -24,6 +24,7 @@ namespace FireflyIII\Repositories\LinkType;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\User;
@@ -188,10 +189,19 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
$link->source()->associate($right);
$link->destination()->associate($left);
}
$link->comment = $link['comments'] ?? null;
$link->save();
// make note in noteable:
if (strlen($information['notes']) > 0) {
$dbNote = $link->notes()->first();
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($link);
}
$dbNote->text = trim($information['notes']);
$dbNote->save();
}
return $link;
}

View File

@@ -126,7 +126,7 @@ class TagRepository implements TagRepositoryInterface
public function findByTag(string $tag): Tag
{
$tags = $this->user->tags()->get();
// @var Tag $tag
/** @var Tag $databaseTag */
foreach ($tags as $databaseTag) {
if ($databaseTag->tag === $tag) {
return $databaseTag;

View File

@@ -128,6 +128,18 @@ class UserRepository implements UserRepositoryInterface
return $this->all()->count();
}
/**
* @param string $name
* @param string $displayName
* @param string $description
*
* @return Role
*/
public function createRole(string $name, string $displayName, string $description): Role
{
return Role::create(['name' => $name, 'display_name' => $displayName, 'description' => $description]);
}
/**
* @param User $user
*
@@ -178,6 +190,16 @@ class UserRepository implements UserRepositoryInterface
return User::first();
}
/**
* @param string $role
*
* @return Role|null
*/
public function getRole(string $role): ?Role
{
return Role::where('name', $role)->first();
}
/**
* Return basic user information.
*
@@ -215,8 +237,8 @@ class UserRepository implements UserRepositoryInterface
->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count();
$return['export_jobs'] = $user->exportJobs()->count();
$return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count();
$return['import_jobs'] = $user->exportJobs()->count();
$return['import_jobs_success'] = $user->exportJobs()->where('status', 'import_complete')->count();
$return['import_jobs'] = $user->importJobs()->count();
$return['import_jobs_success'] = $user->importJobs()->where('status', 'finished')->count();
$return['rule_groups'] = $user->ruleGroups()->count();
$return['rules'] = $user->rules()->count();
$return['tags'] = $user->tags()->count();

View File

@@ -22,6 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\User;
use FireflyIII\Models\Role;
use FireflyIII\User;
use Illuminate\Support\Collection;
@@ -30,6 +31,15 @@ use Illuminate\Support\Collection;
*/
interface UserRepositoryInterface
{
/**
* @param string $name
* @param string $displayName
* @param string $description
*
* @return Role
*/
public function createRole(string $name, string $displayName, string $description): Role;
/**
* Returns a collection of all users.
*
@@ -37,6 +47,13 @@ interface UserRepositoryInterface
*/
public function all(): Collection;
/**
* @param string $role
*
* @return Role|null
*/
public function getRole(string $role): ?Role;
/**
* Gives a user a role.
*

View File

@@ -78,6 +78,14 @@ class Account extends SpectreObject
return $this->id;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return array
*/

View File

@@ -23,19 +23,167 @@ declare(strict_types=1);
namespace FireflyIII\Services\Spectre\Object;
use Carbon\Carbon;
/**
* Class Transaction
*/
class Transaction extends SpectreObject
{
/** @var int */
private $accountId;
/** @var string */
private $amount;
/** @var string */
private $category;
/** @var Carbon */
private $createdAt;
/** @var string */
private $currencyCode;
/** @var string */
private $description;
/** @var bool */
private $duplicated;
/** @var TransactionExtra */
private $extra;
/** @var int */
private $id;
/** @var Carbon */
private $madeOn;
/** @var string */
private $mode;
/** @var string */
private $status;
/** @var Carbon */
private $updatedAt;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* Transaction constructor.
*
* @param array $data
*/
public function __construct(array $data) {
var_dump($data);
exit;
public function __construct(array $data)
{
$this->id = $data['id'];
$this->mode = $data['mode'];
$this->status = $data['status'];
$this->madeOn = new Carbon($data['made_on']);
$this->amount = $data['amount'];
$this->currencyCode = $data['currency_code'];
$this->description = $data['description'];
$this->category = $data['category'];
$this->duplicated = $data['duplicated'];
$this->extra = new TransactionExtra($data['extra'] ?? []);
$this->accountId = $data['account_id'];
$this->createdAt = new Carbon($data['created_at']);
$this->updatedAt = new Carbon($data['updated_at']);
}
/**
* @return string
*/
public function getMode(): string
{
return $this->mode;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @return bool
*/
public function isDuplicated(): bool
{
return $this->duplicated;
}
/**
* @return TransactionExtra
*/
public function getExtra(): TransactionExtra
{
return $this->extra;
}
/**
* @return string
*/
public function getAmount(): string
{
return strval($this->amount);
}
/**
* @return string
*/
public function getCategory(): string
{
return $this->category;
}
/**
* @return string
*/
public function getCurrencyCode(): string
{
return $this->currencyCode;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @return string
*/
public function getHash(): string
{
$array = [
'id' => $this->id,
'mode' => $this->mode,
'status' => $this->status,
'made_on' => $this->madeOn->toIso8601String(),
'amount' => $this->amount,
'currency_code' => $this->currencyCode,
'description' => $this->description,
'category' => $this->category,
'duplicated' => $this->duplicated,
'extra' => $this->extra->toArray(),
'account_id' => $this->accountId,
'created_at' => $this->createdAt->toIso8601String(),
'updated_at' => $this->updatedAt->toIso8601String(),
];
$hashed = hash('sha256', json_encode($array));
return $hashed;
}
/**
* @return Carbon
*/
public function getMadeOn(): Carbon
{
return $this->madeOn;
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* TransactionExtra.php
* Copyright (c) 2018 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\Services\Spectre\Object;
use Carbon\Carbon;
/**
* Class TransactionExtra
*/
class TransactionExtra extends SpectreObject
{
/** @var string */
private $accountBalanceSnapshot;
/** @var string */
private $accountNumber;
/** @var string */
private $additional;
/** @var string */
private $assetAmount;
/** @var string */
private $assetCode;
/** @var string */
private $categorizationConfidence;
/** @var string */
private $checkNumber;
/** @var string */
private $customerCategoryCode;
/** @var string */
private $customerCategoryName;
/** @var string */
private $id;
/** @var string */
private $information;
/** @var string */
private $mcc;
/** @var string */
private $originalAmount;
/** @var string */
private $originalCategory;
/** @var string */
private $originalCurrencyCode;
/** @var string */
private $originalSubCategory;
/** @var string */
private $payee;
/** @var bool */
private $possibleDuplicate;
/** @var Carbon */
private $postingDate;
/** @var Carbon */
private $postingTime;
/** @var string */
private $recordNumber;
/** @var array */
private $tags;
/** @var Carbon */
private $time;
/** @var string */
private $type;
/** @var string */
private $unitPrice;
/** @var string */
private $units;
/**
* TransactionExtra constructor.
*
* @param array $data
*/
public function __construct(array $data)
{
$this->id = $data['id'] ?? null;
$this->recordNumber = $data['record_number'] ?? null;
$this->information = $data['information'] ?? null;
$this->time = isset($data['time']) ? new Carbon($data['time']) : null;
$this->postingDate = isset($data['posting_date']) ? new Carbon($data['posting_date']) : null;
$this->postingTime = isset($data['posting_time']) ? new Carbon($data['posting_time']) : null;
$this->accountNumber = $data['account_number'] ?? null;
$this->originalAmount = $data['original_amount'] ?? null;
$this->originalCurrencyCode = $data['original_currency_code'] ?? null;
$this->assetCode = $data['asset_code'] ?? null;
$this->assetAmount = $data['asset_amount'] ?? null;
$this->originalCategory = $data['original_category'] ?? null;
$this->originalSubCategory = $data['original_subcategory'] ?? null;
$this->customerCategoryCode = $data['customer_category_code'] ?? null;
$this->customerCategoryName = $data['customer_category_name'] ?? null;
$this->possibleDuplicate = $data['possible_duplicate'] ?? null;
$this->tags = $data['tags'] ?? null;
$this->mcc = $data['mcc'] ?? null;
$this->payee = $data['payee'] ?? null;
$this->type = $data['type'] ?? null;
$this->checkNumber = $data['check_number'] ?? null;
$this->units = $data['units'] ?? null;
$this->additional = $data['additional'] ?? null;
$this->unitPrice = $data['unit_price'] ?? null;
$this->accountBalanceSnapshot = $data['account_balance_snapshot'] ?? null;
$this->categorizationConfidence = $data['categorization_confidence'] ?? null;
}
/**
* @return string|null
*/
public function getId(): ?string
{
return $this->id;
}
/**
* @return array
*/
public function toArray(): array
{
$array = [
'id' => $this->id,
'record_number' => $this->recordNumber,
'information' => $this->information,
'time' => is_null($this->time) ? null : $this->time->toIso8601String(),
'posting_date' => is_null($this->postingDate) ? null : $this->postingDate->toIso8601String(),
'posting_time' => is_null($this->postingTime) ? null : $this->postingTime->toIso8601String(),
'account_number' => $this->accountNumber,
'original_amount' => $this->originalAmount,
'original_currency_code' => $this->originalCurrencyCode,
'asset_code' => $this->assetCode,
'asset_amount' => $this->assetAmount,
'original_category' => $this->originalCategory,
'original_subcategory' => $this->originalSubCategory,
'customer_category_code' => $this->customerCategoryCode,
'customer_category_name' => $this->customerCategoryName,
'possible_duplicate' => $this->possibleDuplicate,
'tags' => $this->tags,
'mcc' => $this->mcc,
'payee' => $this->payee,
'type' => $this->type,
'check_number' => $this->checkNumber,
'units' => $this->units,
'additional' => $this->additional,
'unit_price' => $this->unitPrice,
'account_balance_snapshot' => $this->accountBalanceSnapshot,
'categorization_confidence' => $this->categorizationConfidence,
];
return $array;
}
}

View File

@@ -23,9 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Services\Spectre\Request;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Services\Spectre\Exception\SpectreException;
use FireflyIII\Services\Spectre\Object\Account;
use FireflyIII\Services\Spectre\Object\Transaction;
use Log;
/**
* Class ListTransactionsRequest
*/
@@ -37,7 +40,8 @@ class ListTransactionsRequest extends SpectreRequest
private $transactions = [];
/**
*
* @throws FireflyException
* @throws SpectreException
*/
public function call(): void
{
@@ -45,7 +49,7 @@ class ListTransactionsRequest extends SpectreRequest
$nextId = 0;
while ($hasNextPage) {
Log::debug(sprintf('Now calling ListTransactionsRequest for next_id %d', $nextId));
$parameters = ['from_id' => $nextId,'account_id' => $this->account->getId()];
$parameters = ['from_id' => $nextId, 'account_id' => $this->account->getId()];
$uri = '/api/v3/transactions?' . http_build_query($parameters);
$response = $this->sendSignedSpectreGet($uri, []);
@@ -59,7 +63,7 @@ class ListTransactionsRequest extends SpectreRequest
$nextId = $response['meta']['next_id'];
Log::debug(sprintf('Next ID is now %d.', $nextId));
} else {
Log::debug('No next page.');
Log::debug('No next page, done with ListTransactionsRequest.');
}
// store customers:

View File

@@ -45,7 +45,7 @@ class TagList implements BinderInterface
$list = [];
$incoming = explode(',', $value);
foreach ($incoming as $entry) {
$list[] = trim($entry);
$list[] = strtolower(trim($entry));
}
$list = array_unique($list);
if (count($list) === 0) {
@@ -57,7 +57,7 @@ class TagList implements BinderInterface
$collection = $allTags->filter(
function (Tag $tag) use ($list) {
return in_array($tag->tag, $list);
return in_array(strtolower($tag->tag), $list);
}
);

View File

@@ -143,14 +143,13 @@ class Roles implements ConfigurationInterface
$this->saveConfig($config);
$this->ignoreUnmappableColumns();
$this->setRolesComplete();
$config = $this->getConfig();
$config['stage'] = 'map';
$this->saveConfig($config);
$this->isMappingNecessary();
$res = $this->isRolesComplete();
if ($res === true) {
$config = $this->getConfig();
$config['stage'] = 'map';
$this->saveConfig($config);
$this->isMappingNecessary();
}
return true;
}
@@ -225,6 +224,56 @@ class Roles implements ConfigurationInterface
return true;
}
/**
* @return bool
*/
private function isRolesComplete(): bool
{
$config = $this->getConfig();
$count = $config['column-count'];
$assigned = 0;
// check if data actually contains amount column (foreign amount does not count)
$hasAmount = false;
$hasForeignAmount = false;
$hasForeignCode = false;
for ($i = 0; $i < $count; ++$i) {
$role = $config['column-roles'][$i] ?? '_ignore';
if ('_ignore' !== $role) {
++$assigned;
}
if (in_array($role, ['amount', 'amount_credit', 'amount_debit'])) {
$hasAmount = true;
}
if ($role === 'foreign-currency-code') {
$hasForeignCode = true;
}
if ($role === 'amount_foreign') {
$hasForeignAmount = true;
}
}
if ($assigned > 0 && $hasAmount && ($hasForeignCode === false && $hasForeignAmount === false)) {
$this->warning = '';
$this->saveConfig($config);
return true;
}
// warn if has foreign amount but no currency code:
if ($hasForeignAmount && !$hasForeignCode) {
$this->warning = strval(trans('import.foreign_amount_warning'));
return false;
}
if (0 === $assigned || !$hasAmount) {
$this->warning = strval(trans('import.roles_warning'));
return false;
}
return false;
}
/**
* make unique example data.
*/
@@ -284,36 +333,6 @@ class Roles implements ConfigurationInterface
$this->repository->setConfiguration($this->job, $array);
}
/**
* @return bool
*/
private function setRolesComplete(): bool
{
$config = $this->getConfig();
$count = $config['column-count'];
$assigned = 0;
$hasAmount = false;
for ($i = 0; $i < $count; ++$i) {
$role = $config['column-roles'][$i] ?? '_ignore';
if ('_ignore' !== $role) {
++$assigned;
}
if (in_array($role, ['amount', 'amount_credit', 'amount_debit'])) {
$hasAmount = true;
}
}
if ($assigned > 0 && $hasAmount) {
$config['column-roles-complete'] = true;
$this->warning = '';
}
if (0 === $assigned || !$hasAmount) {
$this->warning = strval(trans('import.roles_warning'));
}
$this->saveConfig($config);
return true;
}
/**
* @return bool
*/

View File

@@ -51,7 +51,6 @@ class HaveAccounts implements ConfigurationInterface
$accountRepository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepository */
$currencyRepository = app(CurrencyRepositoryInterface::class);
$data = [];
$config = $this->job->configuration;
$collection = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$defaultCurrency = app('amount')->getDefaultCurrency();

View File

@@ -42,6 +42,27 @@ use Illuminate\Support\Collection;
*/
trait TransactionJournalTrait
{
/**
* @param Builder $query
* @param string $table
*
* @return bool
*/
public static function isJoined(Builder $query, string $table): bool
{
$joins = $query->getQuery()->joins;
if (null === $joins) {
return false;
}
foreach ($joins as $join) {
if ($join->table === $table) {
return true;
}
}
return false;
}
/**
* @return string
*/
@@ -211,27 +232,6 @@ trait TransactionJournalTrait
*/
abstract public function isDeposit(): bool;
/**
* @param Builder $query
* @param string $table
*
* @return bool
*/
public function isJoined(Builder $query, string $table): bool
{
$joins = $query->getQuery()->joins;
if (null === $joins) {
return false;
}
foreach ($joins as $join) {
if ($join->table === $table) {
return true;
}
}
return false;
}
/**
* @return bool
*/

View File

@@ -80,6 +80,68 @@ class Navigation
return $date;
}
/**
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @param string $range
*
* @return array
* @throws FireflyException
*/
public function blockPeriods(\Carbon\Carbon $start, \Carbon\Carbon $end, string $range): array
{
$periods = [];
// Start by looping per period:
$perMonthEnd = clone $end;
$perMonthStart = clone $end;
$perMonthStart->startOfyear()->subYear();
$perMonthStart = $start->lt($perMonthStart) ? $perMonthStart : $start;
// loop first set:
while ($perMonthEnd >= $perMonthStart) {
$perMonthEnd = $this->startOfPeriod($perMonthEnd, $range);
$currentEnd = $this->endOfPeriod($perMonthEnd, $range);
if ($currentEnd->gt($start)) {
$periods[] = [
'start' => $perMonthEnd,
'end' => $currentEnd,
'period' => $range,
];
}
$perMonthEnd = $this->subtractPeriod($perMonthEnd, $range, 1);
}
// do not continue if date is already less
if ($perMonthEnd->lt($start)) {
return $periods;
}
// per year variables:
$perYearEnd = clone $perMonthStart;
$perYearStart = clone $perMonthStart;
unset($perMonthEnd, $currentEnd, $perMonthStart);
$perYearEnd->subYear();
$perYearStart->subYears(50);
$perYearStart = $start->lt($perYearStart) ? $perYearStart : $start;
$perYearStart->startOfYear();
// per year
while ($perYearEnd >= $perYearStart) {
$perYearEnd = $this->startOfPeriod($perYearEnd, '1Y');
$currentEnd = $this->endOfPeriod($perYearEnd, '1Y')->subDay()->endOfDay();
if ($currentEnd->gt($start)) {
$periods[] = [
'start' => $perYearEnd,
'end' => $currentEnd,
'period' => '1Y',
];
}
$perYearEnd = $this->subtractPeriod($perYearEnd, '1Y', 1);
}
return $periods;
}
/**
* @param \Carbon\Carbon $end
* @param $repeatFreq
@@ -194,7 +256,7 @@ class Navigation
{
// define period to increment
$increment = 'addDay';
$format = self::preferredCarbonFormat($start, $end);
$format = $this->preferredCarbonFormat($start, $end);
$displayFormat = strval(trans('config.month_and_day'));
// increment by month (for year)
if ($start->diffInMonths($end) > 1) {
@@ -419,6 +481,7 @@ class Navigation
return $date;
}
if ('custom' === $repeatFreq) {
return $date; // the date is already at the start.
}

View File

@@ -184,11 +184,11 @@ class Steam
$modified = null === $entry->modified ? '0' : strval($entry->modified);
$foreignModified = null === $entry->modified_foreign ? '0' : strval($entry->modified_foreign);
$amount = '0';
if ($currencyId === $entry->transaction_currency_id || 0 === $currencyId) {
if ($currencyId === intval($entry->transaction_currency_id) || 0 === $currencyId) {
// use normal amount:
$amount = $modified;
}
if ($currencyId === $entry->foreign_currency_id) {
if ($currencyId === intval($entry->foreign_currency_id)) {
// use foreign amount:
$amount = $foreignModified;
}

View File

@@ -27,7 +27,6 @@ use FireflyIII\Models\Attachment;
use FireflyIII\Models\Transaction as TransactionModel;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\SingleCacheProperties;
use Lang;
use Twig_Extension;
@@ -45,14 +44,6 @@ class Transaction extends Twig_Extension
*/
public function amount(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-amount');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
$amount = bcmul(app('steam')->positive(strval($transaction->transaction_amount)), '-1');
$format = '%s';
$coloured = true;
@@ -97,8 +88,6 @@ class Transaction extends Twig_Extension
$currency->decimal_places = $transaction->foreign_currency_dp;
$str .= ' (' . sprintf($format, app('amount')->formatAnything($currency, $amount, $coloured)) . ')';
}
$cache->store($str);
return $str;
}
@@ -109,15 +98,6 @@ class Transaction extends Twig_Extension
*/
public function amountArray(array $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-array-amount');
$cache->addProperty($transaction['source_id']);
$cache->addProperty($transaction['destination_id']);
$cache->addProperty($transaction['updated_at']);
if ($cache->has()) {
return $cache->get();
}
// first display amount:
$amount = TransactionType::WITHDRAWAL === $transaction['journal_type'] ? $transaction['source_amount']
: $transaction['destination_amount'];
@@ -135,8 +115,6 @@ class Transaction extends Twig_Extension
$fakeCurrency->symbol = $transaction['foreign_currency_symbol'];
$string .= ' (' . app('amount')->formatAnything($fakeCurrency, $amount, true) . ')';
}
$cache->store($string);
return $string;
}
@@ -147,19 +125,10 @@ class Transaction extends Twig_Extension
*/
public function budgets(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-budgets');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
// journal has a budget:
if (isset($transaction->transaction_journal_budget_id)) {
$name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
$txt = sprintf('<a href="%s" title="%s">%s</a>', route('budgets.show', [$transaction->transaction_journal_budget_id]), $name, $name);
$cache->store($txt);
return $txt;
}
@@ -168,7 +137,6 @@ class Transaction extends Twig_Extension
if (isset($transaction->transaction_budget_id)) {
$name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
$txt = sprintf('<a href="%s" title="%s">%s</a>', route('budgets.show', [$transaction->transaction_budget_id]), $name, $name);
$cache->store($txt);
return $txt;
}
@@ -185,12 +153,10 @@ class Transaction extends Twig_Extension
}
$txt = join(', ', $str);
$cache->store($txt);
return $txt;
}
$txt = '';
$cache->store($txt);
return $txt;
}
@@ -202,19 +168,10 @@ class Transaction extends Twig_Extension
*/
public function categories(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-categories');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
// journal has a category:
if (isset($transaction->transaction_journal_category_id)) {
$name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
$txt = sprintf('<a href="%s" title="%s">%s</a>', route('categories.show', [$transaction->transaction_journal_category_id]), $name, $name);
$cache->store($txt);
return $txt;
}
@@ -223,7 +180,6 @@ class Transaction extends Twig_Extension
if (isset($transaction->transaction_category_id)) {
$name = app('steam')->tryDecrypt($transaction->transaction_category_name);
$txt = sprintf('<a href="%s" title="%s">%s</a>', route('categories.show', [$transaction->transaction_category_id]), $name, $name);
$cache->store($txt);
return $txt;
}
@@ -240,14 +196,11 @@ class Transaction extends Twig_Extension
}
$txt = join(', ', $str);
$cache->store($txt);
return $txt;
}
$txt = '';
$cache->store($txt);
return $txt;
}
@@ -258,20 +211,11 @@ class Transaction extends Twig_Extension
*/
public function description(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('description');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
$description = $transaction->description;
if (strlen(strval($transaction->transaction_description)) > 0) {
$description = $transaction->transaction_description . '(' . $transaction->description . ')';
$description = $transaction->transaction_description . ' (' . $transaction->description . ')';
}
$cache->store($description);
return $description;
}
@@ -282,14 +226,6 @@ class Transaction extends Twig_Extension
*/
public function destinationAccount(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-destination');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
if (TransactionType::RECONCILIATION === $transaction->transaction_type_type) {
return '&mdash;';
}
@@ -325,13 +261,11 @@ class Transaction extends Twig_Extension
if (AccountType::CASH === $type) {
$txt = '<span class="text-success">(' . trans('firefly.cash') . ')</span>';
$cache->store($txt);
return $txt;
}
$txt = sprintf('<a title="%1$s" href="%2$s">%1$s</a>', e($name), route('accounts.show', [$transactionId]));
$cache->store($txt);
return $txt;
}
@@ -343,13 +277,6 @@ class Transaction extends Twig_Extension
*/
public function hasAttachments(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('attachments');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
$journalId = intval($transaction->journal_id);
$count = Attachment::whereNull('deleted_at')
->where('attachable_type', 'FireflyIII\Models\TransactionJournal')
@@ -357,13 +284,11 @@ class Transaction extends Twig_Extension
->count();
if ($count > 0) {
$res = sprintf('<i class="fa fa-paperclip" title="%s"></i>', Lang::choice('firefly.nr_of_attachments', $count, ['count' => $count]));
$cache->store($res);
return $res;
}
$res = '';
$cache->store($res);
return $res;
}
@@ -375,14 +300,6 @@ class Transaction extends Twig_Extension
*/
public function icon(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('icon');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
switch ($transaction->transaction_type_type) {
case TransactionType::WITHDRAWAL:
$txt = sprintf('<i class="fa fa-long-arrow-left fa-fw" title="%s"></i>', trans('firefly.withdrawal'));
@@ -403,7 +320,6 @@ class Transaction extends Twig_Extension
$txt = '';
break;
}
$cache->store($txt);
return $txt;
}
@@ -415,21 +331,11 @@ class Transaction extends Twig_Extension
*/
public function isReconciled(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-reconciled');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
$cache->addProperty($transaction->reconciled);
if ($cache->has()) {
return $cache->get();
}
$icon = '';
if (1 === intval($transaction->reconciled)) {
$icon = '<i class="fa fa-check"></i>';
}
$cache->store($icon);
return $icon;
}
@@ -440,25 +346,14 @@ class Transaction extends Twig_Extension
*/
public function isSplit(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('split');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
$journalId = intval($transaction->journal_id);
$count = TransactionModel::where('transaction_journal_id', $journalId)->whereNull('deleted_at')->count();
if ($count > 2) {
$res = '<i class="fa fa-fw fa-share-alt" aria-hidden="true"></i>';
$cache->store($res);
return $res;
}
$res = '';
$cache->store($res);
return $res;
}
@@ -469,13 +364,6 @@ class Transaction extends Twig_Extension
*/
public function sourceAccount(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-source');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
if (TransactionType::RECONCILIATION === $transaction->transaction_type_type) {
return '&mdash;';
}
@@ -510,14 +398,11 @@ class Transaction extends Twig_Extension
if (AccountType::CASH === $type) {
$txt = '<span class="text-success">(' . trans('firefly.cash') . ')</span>';
$cache->store($txt);
return $txt;
}
$txt = sprintf('<a title="%1$s" href="%2$s">%1$s</a>', e($name), route('accounts.show', [$transactionId]));
$cache->store($txt);
return $txt;
}
}

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\Support\Twig\Extension;
use FireflyIII\Models\Transaction as TransactionModel;
use FireflyIII\Models\TransactionJournal as JournalModel;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\SingleCacheProperties;
use Twig_Extension;
/**
@@ -40,14 +39,6 @@ class TransactionJournal extends Twig_Extension
*/
public function totalAmount(JournalModel $journal): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('total-amount');
$cache->addProperty($journal->id);
$cache->addProperty($journal->updated_at);
if ($cache->has()) {
return $cache->get();
}
$transactions = $journal->transactions()->where('amount', '>', 0)->get();
$totals = [];
$type = $journal->transactionType->type;
@@ -84,7 +75,6 @@ class TransactionJournal extends Twig_Extension
$array[] = app('amount')->formatAnything($total['currency'], $total['amount']);
}
$txt = join(' / ', $array);
$cache->store($txt);
return $txt;
}

View File

@@ -256,9 +256,64 @@ class General extends Twig_Extension
return 'fa-file-o';
case 'application/pdf':
return 'fa-file-pdf-o';
/* image */
case 'image/png':
case 'image/jpeg':
case 'image/svg+xml':
case 'image/heic':
case 'image/heic-sequence':
case 'application/vnd.oasis.opendocument.image':
return 'fa-file-image-o';
/* MS word */
case 'application/msword':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template':
case 'application/x-iwork-pages-sffpages':
case 'application/vnd.sun.xml.writer':
case 'application/vnd.sun.xml.writer.template':
case 'application/vnd.sun.xml.writer.global':
case 'application/vnd.stardivision.writer':
case 'application/vnd.stardivision.writer-global':
case 'application/vnd.oasis.opendocument.text':
case 'application/vnd.oasis.opendocument.text-template':
case 'application/vnd.oasis.opendocument.text-web':
case 'application/vnd.oasis.opendocument.text-master':
return 'fa-file-word-o';
/* MS excel */
case 'application/vnd.ms-excel':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
case 'application/vnd.sun.xml.calc':
case 'application/vnd.sun.xml.calc.template':
case 'application/vnd.stardivision.calc':
case 'application/vnd.oasis.opendocument.spreadsheet':
case 'application/vnd.oasis.opendocument.spreadsheet-template':
return 'fa-file-excel-o';
/* MS powerpoint */
case 'application/vnd.ms-powerpoint':
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
case 'application/vnd.openxmlformats-officedocument.presentationml.template':
case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
case 'application/vnd.sun.xml.impress':
case 'application/vnd.sun.xml.impress.template':
case 'application/vnd.stardivision.impress':
case 'application/vnd.oasis.opendocument.presentation':
case 'application/vnd.oasis.opendocument.presentation-template':
return 'fa-file-powerpoint-o';
/* calc */
case 'application/vnd.sun.xml.draw':
case 'application/vnd.sun.xml.draw.template':
case 'application/vnd.stardivision.draw':
case 'application/vnd.oasis.opendocument.chart':
return 'fa-paint-brush';
case 'application/vnd.oasis.opendocument.graphics':
case 'application/vnd.oasis.opendocument.graphics-template':
case 'application/vnd.sun.xml.math':
case 'application/vnd.stardivision.math':
case 'application/vnd.oasis.opendocument.formula':
case 'application/vnd.oasis.opendocument.database':
return 'fa-calculator';
}
},
['is_safe' => ['html']]

View File

@@ -32,6 +32,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Log;
use Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class User.
@@ -60,6 +61,23 @@ class User extends Authenticatable
*/
protected $table = 'users';
/**
* @param string $value
*
* @return User
*/
public static function routeBinder(string $value): User
{
if (auth()->check()) {
$userId = intval($value);
$user = self::find($userId);
if (!is_null($user)) {
return $user;
}
}
throw new NotFoundHttpException;
}
/**
* @codeCoverageIgnore
* Link to accounts.

View File

@@ -65,7 +65,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validate2faCode($attribute, $value): bool
public function validate2faCode(/** @scrutinizer ignore-unused */ $attribute, $value): bool
{
if (!is_string($value) || null === $value || 6 != strlen($value)) {
return false;
@@ -85,7 +85,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateBelongsToUser($attribute, $value, $parameters): bool
public function validateBelongsToUser(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
$field = $parameters[1] ?? 'id';
@@ -108,7 +108,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateBic($attribute, $value): bool
public function validateBic(/** @scrutinizer ignore-unused */ $attribute, $value): bool
{
$regex = '/^[a-z]{6}[0-9a-z]{2}([0-9a-z]{3})?\z/i';
$result = preg_match($regex, $value);
@@ -130,13 +130,63 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateIban($attribute, $value): bool
public function validateIban(/** @scrutinizer ignore-unused */ $attribute, $value): bool
{
if (!is_string($value) || null === $value || strlen($value) < 6) {
return false;
}
$value = strtoupper($value);
// strip spaces
$search = [
"\x20", // normal space
"\u{0001}", // start of heading
"\u{0002}", // start of text
"\u{0003}", // end of text
"\u{0004}", // end of transmission
"\u{0005}", // enquiry
"\u{0006}", // ACK
"\u{0007}", // BEL
"\u{0008}", // backspace
"\u{000E}", // shift out
"\u{000F}", // shift in
"\u{0010}", // data link escape
"\u{0011}", // DC1
"\u{0012}", // DC2
"\u{0013}", // DC3
"\u{0014}", // DC4
"\u{0015}", // NAK
"\u{0016}", // SYN
"\u{0017}", // ETB
"\u{0018}", // CAN
"\u{0019}", // EM
"\u{001A}", // SUB
"\u{001B}", // escape
"\u{001C}", // file separator
"\u{001D}", // group separator
"\u{001E}", // record separator
"\u{001F}", // unit separator
"\u{007F}", // DEL
"\u{00A0}", // non-breaking space
"\u{1680}", // ogham space mark
"\u{180E}", // mongolian vowel separator
"\u{2000}", // en quad
"\u{2001}", // em quad
"\u{2002}", // en space
"\u{2003}", // em space
"\u{2004}", // three-per-em space
"\u{2005}", // four-per-em space
"\u{2006}", // six-per-em space
"\u{2007}", // figure space
"\u{2008}", // punctuation space
"\u{2009}", // thin space
"\u{200A}", // hair space
"\u{200B}", // zero width space
"\u{202F}", // narrow no-break space
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
];
$replace = '';
$value = str_replace($search, $replace, $value);
$value = strtoupper($value);
$search = [' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
$replace = ['', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
@@ -160,7 +210,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateMore($attribute, $value, $parameters): bool
public function validateMore(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
$compare = $parameters[0] ?? '0';
@@ -176,7 +226,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateMustExist($attribute, $value, $parameters): bool
public function validateMustExist(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
$field = $parameters[1] ?? 'id';
@@ -285,7 +335,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateSecurePassword($attribute, $value): bool
public function validateSecurePassword(/** @scrutinizer ignore-unused */ $attribute, $value): bool
{
$verify = false;
if (isset($this->data['verify_password'])) {
@@ -310,7 +360,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateUniqueAccountForUser($attribute, $value, $parameters): bool
public function validateUniqueAccountForUser(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
// because a user does not have to be logged in (tests and what-not).
if (!auth()->check()) {
@@ -340,7 +390,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateUniqueAccountNumberForUser($attribute, $value): bool
public function validateUniqueAccountNumberForUser(/** @scrutinizer ignore-unused */ $attribute, $value): bool
{
$accountId = $this->data['id'] ?? 0;
@@ -378,7 +428,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateUniqueObjectForUser($attribute, $value, $parameters): bool
public function validateUniqueObjectForUser(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
$value = $this->tryDecrypt($value);
// exclude?
@@ -410,7 +460,7 @@ class FireflyValidator extends Validator
*
* @return bool
*/
public function validateUniquePiggyBankForUser($attribute, $value, $parameters): bool
public function validateUniquePiggyBankForUser(/** @scrutinizer ignore-unused */ $attribute, $value, $parameters): bool
{
$exclude = $parameters[0] ?? null;
$query = DB::table('piggy_banks')->whereNull('piggy_banks.deleted_at')

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