mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-16 09:22:33 +00:00
Compare commits
145 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
735222a8ed | ||
|
66f299cd06 | ||
|
e3c5268143 | ||
|
df32493d77 | ||
|
86faf44153 | ||
|
e2f3e4b555 | ||
|
e57ed6015c | ||
|
9c34ca7fc4 | ||
|
7b94f4a441 | ||
|
693f8d0738 | ||
|
d6ecbc06bf | ||
|
c31674fffc | ||
|
b08de8cc00 | ||
|
4c503e4c7c | ||
|
dd4158c6b4 | ||
|
5fd7ea2b96 | ||
|
b9ff80eb5a | ||
|
afc725bbc8 | ||
|
0342c371cc | ||
|
2da4e6b048 | ||
|
f50550d79c | ||
|
3fa39a6805 | ||
|
3dbe6d4870 | ||
|
59c48268ab | ||
|
1f83c5195d | ||
|
49a95a08fe | ||
|
c86c5ccfe9 | ||
|
8a2497fc67 | ||
|
7c70732247 | ||
|
53fc4f2740 | ||
|
f3ade5621e | ||
|
ec2e08e33a | ||
|
b9a26faa4d | ||
|
1a434d0c83 | ||
|
9a2c6c2967 | ||
|
602b35d589 | ||
|
f42cd0c7c3 | ||
|
cb81855a17 | ||
|
89cf351ad0 | ||
|
3f70a3f06c | ||
|
9df360f010 | ||
|
9a26d6d49f | ||
|
bc4d801c12 | ||
|
f2d8e13576 | ||
|
ec0b5db973 | ||
|
46a0d1ce35 | ||
|
cb81446ca6 | ||
|
9350b4939c | ||
|
33e8c8c415 | ||
|
48fa86cc54 | ||
|
d5e6d1c578 | ||
|
0bc688795a | ||
|
eb76ed5591 | ||
|
839cbaf37a | ||
|
788fc9204d | ||
|
3e3e304ef3 | ||
|
447d453fdc | ||
|
36fd7884f3 | ||
|
54da08b2f2 | ||
|
3f02072ae9 | ||
|
a9c117703b | ||
|
c137255155 | ||
|
e7829ecc38 | ||
|
529bdafa31 | ||
|
e2af0caa41 | ||
|
80f96abf08 | ||
|
70da38193f | ||
|
13df973873 | ||
|
3ccb791674 | ||
|
ccf1a6c182 | ||
|
493543c1f5 | ||
|
5f5725e0e3 | ||
|
107dd42957 | ||
|
a9f3fe4d3a | ||
|
3e62e17b9e | ||
|
57855b1930 | ||
|
aa9e8227bb | ||
|
a80f083b6e | ||
|
474e066d4a | ||
|
4428ccefbf | ||
|
d568a6c8a9 | ||
|
97e9ad6cb2 | ||
|
00607d2a6d | ||
|
c2a425121d | ||
|
435694e9ea | ||
|
f59135a9ca | ||
|
102b106402 | ||
|
5c27c8e633 | ||
|
edd5215b21 | ||
|
94b173ae6b | ||
|
7d96b281b6 | ||
|
a5515ac89f | ||
|
fb863b0bf2 | ||
|
50882f309b | ||
|
ce854fbb43 | ||
|
6799268ec4 | ||
|
6fe5ce0485 | ||
|
cbeaf8e16a | ||
|
04de4c9b36 | ||
|
517731cb59 | ||
|
e34e43173c | ||
|
79d6055a78 | ||
|
7ac4d2a2f4 | ||
|
4984eda320 | ||
|
89e0791e2f | ||
|
922d487821 | ||
|
4b789979ac | ||
|
554b38ccff | ||
|
9614310208 | ||
|
d9ec3ac354 | ||
|
f326f08f7b | ||
|
0ae8418f32 | ||
|
309f9cd076 | ||
|
61f5ed3874 | ||
|
91178d2604 | ||
|
87dae6ea18 | ||
|
2e495c38d1 | ||
|
c2987aaf4c | ||
|
f4f4eecb7b | ||
|
84d9287251 | ||
|
b71f334744 | ||
|
ad306e4d01 | ||
|
e40d4ef829 | ||
|
48c16c3dcc | ||
|
c045193246 | ||
|
a816e59a97 | ||
|
892074eaf2 | ||
|
3e501e429d | ||
|
0f40accb4c | ||
|
3dae6c99a4 | ||
|
b185c83597 | ||
|
ef6b4120f1 | ||
|
a43eef01fc | ||
|
2cb9aa537f | ||
|
2edd49a8b4 | ||
|
a57554d380 | ||
|
c89486b6d9 | ||
|
f737cb7235 | ||
|
f1fe169553 | ||
|
2fc760780e | ||
|
8c3290bf6f | ||
|
495158b9c9 | ||
|
f9fc9b1889 | ||
|
11ff2ab9d1 | ||
|
52b138e6b2 |
12
.codeclimate.yml
Normal file
12
.codeclimate.yml
Normal 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/
|
72
.env.docker
72
.env.docker
@@ -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
|
||||
|
64
.env.example
64
.env.example
@@ -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
|
||||
|
72
.env.heroku
72
.env.heroku
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
0
.github/SUPPORT.md → .github/support.md
vendored
0
.github/SUPPORT.md → .github/support.md
vendored
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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;"
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
|
||||
|
105
.scrutinizer.yml
105
.scrutinizer.yml
@@ -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
|
||||
|
@@ -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:
|
||||
|
89
README.md
89
README.md
@@ -1,89 +0,0 @@
|
||||
# Firefly III: A personal finances manager
|
||||
|
||||
[](https://secure.php.net/downloads.php) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://www.gnu.org/licenses/gpl.html) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/index.png) [](https://firefly-iii.org/static/screenshots/4.6.12/account.png)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/budget.png) [](https://firefly-iii.org/static/screenshots/4.6.12/category.png)
|
||||
|
||||
[](https://firefly-iii.org/static/screenshots/4.6.12/report1.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
|
||||
|
||||
[](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", it’s just somebody else’s 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).
|
||||
|
||||
[](https://travis-ci.org/firefly-iii/firefly-iii) [](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [](https://coveralls.io/github/firefly-iii/firefly-iii?branch=master)
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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');
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
@@ -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'));
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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 = ' ';
|
||||
/** @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;';
|
||||
}
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ class ConfigurationController extends Controller
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
$this->middleware(IsDemoUser::class)->except(['index']);
|
||||
$this->middleware(IsDemoUser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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'));
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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'));
|
||||
|
@@ -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 = [];
|
||||
|
@@ -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])) {
|
||||
|
@@ -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',
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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])) {
|
||||
|
@@ -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.');
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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');
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
@@ -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)) {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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()');
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -30,6 +30,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* Class ExportJob.
|
||||
*
|
||||
* @property User $user
|
||||
* @property string $key
|
||||
*/
|
||||
class ExportJob extends Model
|
||||
{
|
||||
|
@@ -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']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -28,6 +28,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class TransactionCurrency.
|
||||
*
|
||||
* @property string $code
|
||||
*
|
||||
*/
|
||||
class TransactionCurrency extends Model
|
||||
{
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -699,6 +699,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return $iban;
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -78,6 +78,14 @@ class Account extends SpectreObject
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
169
app/Services/Spectre/Object/TransactionExtra.php
Normal file
169
app/Services/Spectre/Object/TransactionExtra.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -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:
|
||||
|
@@ -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);
|
||||
}
|
||||
);
|
||||
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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.
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 '—';
|
||||
}
|
||||
@@ -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 '—';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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']]
|
||||
|
18
app/User.php
18
app/User.php
@@ -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.
|
||||
|
@@ -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
Reference in New Issue
Block a user